2 minute read

学んだこと

管理者ユーザを想定してフラグを Context に保持する

  • LoginUserProvider.tsx
    • 管理者ユーザを ID10 にする。
import React, {
  createContext,
  Dispatch,
  SetStateAction,
  ReactNode,
  useState,
} from "react";
import { User } from "../types/api/user";

// ログインユーザでisAdminを追加する
type LoginUser = User & { isAdmin: boolean };

export type LoginUserContextType = {
  loginUser: LoginUser | undefined;
  setLoginUser: Dispatch<SetStateAction<LoginUser | undefined>>;
};

export const LoginUserContext = createContext<LoginUserContextType>(
  {} as LoginUserContextType
);

export const LoginUserProvider = (props: { children: ReactNode }) => {
  const { children } = props;
  const [loginUser, setLoginUser] = useState<User | undefined>(undefined);
  return (
    <LoginUserContext.Provider value=>
      {children}
    </LoginUserContext.Provider>
  );
};

  • useAuth.ts
    • ログイン時の処理で、id=10 のユーザは isAdmin を true にし仮に管理者としている。
import axios from "axios";
import { useCallback, useState } from "react";
import { User } from "../types/api/user";
import { useHistory } from "react-router-dom";
import { useMessage } from "./useMessage";
import { useLoginUser } from "./useLoginUser";

export const useAuth = () => {
  const history = useHistory();
  const { showMessage } = useMessage();
  const [loading, setLoading] = useState(false);
  const { setLoginUser } = useLoginUser();
  const login = useCallback(
    (id: string) => {
      setLoading(true);
      axios
        .get<User>(`https://jsonplaceholder.typicode.com/users/${id}`)
        .then((res) => {
          if (res.data) {
            const isAdmin = res.data.id === 10 ? true : false;
            setLoginUser({ ...res.data, isAdmin });
            showMessage({ title: "ログインしました", status: "success" });
            history.push("/home");
          } else {
            showMessage({
              title: "ユーザーが見つかりませんでした",
              status: "error",
            });
            setLoading(false);
          }
        })
        .catch(() => {
          showMessage({
            title: "ログイン出来ませんでした",
            status: "error",
          });
          setLoading(false);
        });
    },
    [history, showMessage, setLoginUser]
  );

  return { login, loading };
};

管理者ユーザのみユーザ情報を編集できるようにする

  • UserDetailModal.tsx
    • ユーザ情報が記載されているモーダルにて、更新ボタンを作成
      • 更新ボタンは管理者ユーザのみ表示される
import React, { memo, VFC, useState, useEffect, ChangeEvent } from "react";
import {
  Stack,
  Modal,
  ModalOverlay,
  ModalContent,
  ModalHeader,
  ModalCloseButton,
  ModalBody,
  FormControl,
  FormLabel,
  Input,
  ModalFooter,
} from "@chakra-ui/react";
import { User } from "../../../types/api/user";
import { PrimaryButton } from "../../atoms/button/PrimaryButton";

type Props = {
  user: User | undefined;
  isOpen: boolean;
  onClose: () => void;
  isAdmin: boolean;
};

export const UserDetailModal: VFC<Props> = memo((props) => {
  const { user, isOpen, onClose, isAdmin } = props;
  const [username, setUsername] = useState("");
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [phone, setPhone] = useState("");

  useEffect(() => {
    setUsername(user?.username ?? "");
    setName(user?.name ?? "");
    setEmail(user?.email ?? "");
    setPhone(user?.phone ?? "");
  }, [user]);

  const onChangeUserName = (e: ChangeEvent<HTMLInputElement>) =>
    setUsername(e.target.value);
  const onChangeName = (e: ChangeEvent<HTMLInputElement>) =>
    setName(e.target.value);
  const onChangeEmail = (e: ChangeEvent<HTMLInputElement>) =>
    setEmail(e.target.value);
  const onChangePhone = (e: ChangeEvent<HTMLInputElement>) =>
    setPhone(e.target.value);

  const onClickUpdate = () => alert();

  return (
    <Modal
      isOpen={isOpen}
      onClose={onClose}
      autoFocus={false}
      motionPreset="slideInBottom"
    >
      <ModalOverlay />
      <ModalContent pb={6}>
        <ModalHeader>ユーザー詳細</ModalHeader>
        <ModalCloseButton />
        <ModalBody mx={4}>
          <Stack spacing={4}>
            <FormControl>
              <FormLabel>名前</FormLabel>
              <Input
                value={username}
                onChange={onChangeUserName}
                isReadOnly={!isAdmin}
              />
              <FormLabel>フルネーム</FormLabel>
              <Input
                value={name}
                onChange={onChangeName}
                isReadOnly={!isAdmin}
              />
              <FormLabel>MAIL</FormLabel>
              <Input
                value={email}
                onChange={onChangeEmail}
                isReadOnly={!isAdmin}
              />
              <FormLabel>TEL</FormLabel>
              <Input
                value={phone}
                onChange={onChangePhone}
                isReadOnly={!isAdmin}
              />
            </FormControl>
          </Stack>
          {isAdmin && (
            <ModalFooter>
              <PrimaryButton onClick={onClickUpdate}>更新</PrimaryButton>
            </ModalFooter>
          )}
        </ModalBody>
      </ModalContent>
    </Modal>
  );
});

全体を通して

  • ChakraUI は直感的でわかりやすい
    • エディタの予測変換に出てくるので、やりやすい
  • TypeScrpt は少しだが、C++や C#の型が厳密に決めなければいけない言語と似ていてやりやすかった。
    • 逆に JavaScript は型を厳密に決めないため、少し困惑した。
  • 簡単なアプリを Udemy で見ながら作ってみたものの、理解しているかどうか不明
    • 自分で簡単なアプリ 1 つは作らないと身についているかどうか分からない。
  • AtomicDesign はとても画期的だった
    • 今までプログラミングをおこなってきたが、AtomicDesign を使えばもっときれいにソース管理出来たのではないか?と思ってしまった。
    • 実際に自分がソースを作成する際、AtomicDesign 使ってみたい。