useState와 use Reducter는 사실상 큰 차이가 있어요.

개시하다


도쿄도 내에서 엔지니어로 개발하다.
구체적인 기술 창고는 다음과 같다.
  • React.js
  • Next.js
  • TypeScript
  • GraphQL(Apollo)
  • 이번에는 React.jsHooksuseState의 차이에 대해 여러 가지 설명을 드리겠습니다.

    제가 이 기사를 쓴 계기가 되고 싶어요.


    지금까지 저는 useReducer를 이용하여 기본적으로 개발하고 처리useState의 기회가 없습니다.useReducer로 복잡한 화면을 설치하면 품질을 보장할 수 없기 때문에 버그가 발생했습니다.(나는 작가의 실장 능력이 부족하다고 생각한다.)
    그러나 같은 화면useState을 다시 쓰면서 품질을 보증할 수 있고, 사회적으로 소개된 것useReducer과는 다른 장점을 봤기 때문에 보내기로 했다.
    학습useReducer과정React.js초보자에게는 처리하기 어렵고useReducer도 같은 처리를 할 수 있기 때문에 많은 분들이 경원하시죠?
    그런 여러분들.
  • useStateuseState의 차이점을 똑똑히 알았다
  • 설치된 화면 내용에 따라 useReducer 사용해 보세요
  • 이런 기분 드셨으면 좋겠습니다.

    이 문장의 대상


  • 숙지useReducerReact.js 기본 설치

  • 이해TypeScript의 단어

  • 전혀 몰라単体テストuseState의 큰 차이
  • 이번에 만든 기능의 필수 조건.


    소개useReduceruseState를 위해 표를 제작합니다.
    구체적인 요건은 아래와 같다.
  • 연락처 등록 양식
  • 사용자가 최대 5개의 연락처를 추가할 수 있음
  • 연락처의 설정은 다음과 같습니다.
  • 열람 모드
  • 메일 주소를 편집할 수 없음
  • 편집 버튼을 눌러 편집 모드로 전환
  • 편집 모드
  • 이메일 주소 편집 가능
  • 메일 주소 저장 가능
  • 저장하려면 누르면 열람 모드
  • 이메일 주소 삭제 가능
  • 성과물


    이번 성과물은 useReducer에 있다.
    글에서도 일부분을 소개했으니 여기.에서 실제 동작을 확인해 주십시오

    미리 준비하다

  • 제작 준비 codesandbox閲覧モード 구성 요소
  • import React from "react";
    
    type Props = {
      index: number;
      value: string;
      onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
      onSave: () => void;
      onDelete: () => void;
    };
    
    export const EditNotificationDestination: React.FC<Props> = ({
      index,
      value,
      onChange,
      onSave,
      onDelete,
    }) => {
      return (
        <div>
          <span>通知先{index}</span>
          <input value={value} onChange={onChange} />
          <button onClick={onSave}>保存</button>
          <button onClick={onDelete}>削除</button>
        </div>
      );
    };
    
    import React from "react";
    
    type Props = {
      value: string;
      onClick: () => void;
    };
    
    export const ViewNotificationDestination: React.FC<Props> = ({
      value,
      onClick,
    }) => {
      return (
        <div>
          <span>{value}</span>
          <button onClick={onClick}>編集</button>
        </div>
      );
    };
    

    useState에서 설치


    우선編集モード.useState 공부를 시작하면 먼저 Hooks를 기억해야 한다.
    여기를 이용하여 논리를 엮은 것은 다음과 같다.
    import React, { useState } from "react";
    import { EditNotificationDestination } from "../components/EditNotificationDestination";
    import { ViewNotificationDestination } from "../components/ViewNotificationDestination";
    import { DisplayMode, NotificationDestination } from "./UseReducer";
    
    export const UseState = () => {
      const [notifications, setNotifications] = useState<NotificationDestination[]>(
        []
      );
      const isNotificationMaximum = notifications.length >= 5;
    
      const changeNotificationDisplayMode = (
        index: number,
        displayMode: DisplayMode
      ) => {
        const newNotifications = notifications.map((o, mIndex) => {
          return index === mIndex ? { displayMode, email: o.email } : o;
        });
        setNotifications(newNotifications);
      };
    
      const addNewNotification = () => {
        setNotifications([...notifications, { displayMode: "edit", email: "" }]);
      };
      const updateNotificationEmail = (
        index: number,
        e: React.ChangeEvent<HTMLInputElement>
      ) => {
        const newNotifications = notifications.map((o, mIndex) => {
          return index === mIndex
            ? { displayMode: o.displayMode, email: e.target.value }
            : o;
        });
        setNotifications(newNotifications);
      };
    
      const deleteNotification = (index: number) => {
        const notificationsExcludedIndex = notifications.filter(
          (o, fIndex) => index !== fIndex
        );
        setNotifications(notificationsExcludedIndex);
      };
    
      return (
        <div className="App">
          {notifications.map((o, mIndex) => (
            <div key={mIndex}>
              {o.displayMode === "edit" ? (
                <EditNotificationDestination
                  index={mIndex + 1}
                  value={o.email}
                  onChange={(e) => updateNotificationEmail(mIndex, e)}
                  onDelete={() => deleteNotification(mIndex)}
                  onSave={() => changeNotificationDisplayMode(mIndex, "view")}
                />
              ) : (
                <ViewNotificationDestination
                  value={o.email}
                  onClick={() => changeNotificationDisplayMode(mIndex, "edit")}
                />
              )}
            </div>
          ))}
    
          {!isNotificationMaximum ? (
            <button onClick={addNewNotification}>追加</button>
          ) : null}
        </div>
      );
    };
    
    이것은 흔히 볼 수 있는 실시 방안이다.
    이것을 파악한 토대React.js에서 보자.

    useReducter에서 설치


    다음은 useReducer를 이용하여 실시한 것이다.
    UseReducer.tsx
    import { EditNotificationDestination } from "../components/EditNotificationDestination";
    import { ViewNotificationDestination } from "../components/ViewNotificationDestination";
    import { useReducer } from "react";
    import { reducer } from "../reducer/reducer";
    
    export type DisplayMode = "view" | "edit";
    export type NotificationDestination = {
      displayMode: DisplayMode;
      email: string;
    };
    
    const initialState: NotificationDestination[] = [];
    
    export const UseReducer = () => {
      const [notifications, dispatch] = useReducer(reducer, initialState);
    
      const isNotificationMaximum = notifications.length >= 5;
      return (
        <div className="App">
          {notifications.map((o, mIndex) => (
            <div key={mIndex}>
              {o.displayMode === "edit" ? (
                <EditNotificationDestination
                  index={mIndex + 1}
                  value={o.email}
                  onChange={(e) =>
                    dispatch({
                      type: "updateNotification",
                      payload: { index: mIndex, value: e.target.value },
                    })
                  }
                  onDelete={() =>
                    dispatch({
                      type: "deleteNotification",
                      payload: { index: mIndex },
                    })
                  }
                  onSave={() =>
                    dispatch({
                      type: "changeDisplayMode",
                      payload: { index: mIndex, displayMode: "view" },
                    })
                  }
                />
              ) : (
                <ViewNotificationDestination
                  value={o.email}
                  onClick={() =>
                    dispatch({
                      type: "changeDisplayMode",
                      payload: { index: mIndex, displayMode: "edit" },
                    })
                  }
                />
              )}
            </div>
          ))}
    
          {!isNotificationMaximum ? (
            <button onClick={() => dispatch({ type: "newNotification" })}>
              追加
            </button>
          ) : null}
        </div>
      );
    };
    
    
    reducer.ts
    import { NotificationDestination, DisplayMode } from "../pages/UseReducer";
    
    type Actions =
      | {
          type: "changeDisplayMode";
          payload: { index: number; displayMode: DisplayMode };
        }
      | { type: "newNotification" }
      | { type: "updateNotification"; payload: { index: number; value: string } }
      | { type: "fetchAllNotification"; payload: { values: string[] } }
      | { type: "deleteNotification"; payload: { index: number } };
    
    export const reducer = (
      state: NotificationDestination[],
      action: Actions
    ): NotificationDestination[] => {
      switch (action.type) {
        case "changeDisplayMode": {
          const { index, displayMode } = action.payload;
    
          return state.map((o, mIndex) => {
            return mIndex === index
              ? { displayMode: displayMode, email: o.email }
              : o;
          });
        }
    
        case "newNotification": {
          return [...state, { displayMode: "edit", email: "" }];
        }
    
        case "updateNotification": {
          const { index, value } = action.payload;
          return state.map((o, mIndex) => {
            return mIndex === index
              ? { displayMode: o.displayMode, email: value }
              : o;
          });
        }
    
        case "fetchAllNotification": {
          const { values } = action.payload;
          const newState: NotificationDestination[] = values.map((o) => {
            return {
              displayMode: "view",
              email: o,
            };
          });
          return newState;
        }
    
        case "deleteNotification": {
          const newState: NotificationDestination[] = state.filter(
            (o, index) => action.payload.index !== index
          );
          return newState;
        }
      }
    };
    
    
    useReducer의 기본 설치 방법에 관해서는 공식 문서를 참조하십시오.
    특정 useReducer에 따라 action에서 업데이트reducer를 통해 설치 내용을 쉽게 볼 수 있습니다.

    use Reducter의 강점은요.


    지금까지 stateuseState를 실물로 비교했다.useReducer정의useReducer,action이기 때문에 다음과 같은 장점이 있다.
  • state의 변화 논리는 분리될 수 있음reducer
  • reducer, 기재해야 하기 때문에action 논리적인 명문화는reducer보다 더 기재된다
  • 그러나 저자는 이런 장점이 useState이 가장 강하지 않다고 생각한다.
    그렇다면 useReducer 가장 강한 곳은 무엇일까?여기를 이해하기 위해서 아래 코드를 다시 한 번 봅시다.
    reducer.ts
    
    export const reducer = (
      state: NotificationDestination[],
      action: Actions
    ): NotificationDestination[] => {
      switch (action.type) {
        case "changeDisplayMode": {
          const { index, displayMode } = action.payload;
    
          return state.map((o, mIndex) => {
            return mIndex === index
              ? { displayMode: displayMode, email: o.email }
              : o;
          });
        }
        // 以下省略
      }
    };
    
    자세히 보면 useReducer수신reducer.tsstate의 순함수로 acitonuseReducerstate와는 무관하다.
    의존하지 않는 순수함수이기 때문에 state의 논리에 대한 단일 테스트를 쓸 수 있습니다.
    저자는 복잡한 논리와 복잡한 화면일수록 단일 테스트를 실시할수록 오류를 사전에 방지할 수 있다고 본다.
    따라서state와 관련된 단일 테스트를 하는 것은 매우 중요하다.
    구체적인 테스트 코드: reducer.ts는 다음과 같다.
    reducer.test.ts
    import { reducer } from "./reducer";
    import { NotificationDestination } from "../pages/UseReducer";
    
    describe("reducerのテスト", () => {
      let notifications: NotificationDestination[] = [
        { displayMode: "view", email: "[email protected]" },
        { displayMode: "view", email: "[email protected]" },
        { displayMode: "view", email: "[email protected]" },
      ];
    
      beforeEach(() => {
        notifications = [
          { displayMode: "view", email: "[email protected]" },
          { displayMode: "view", email: "[email protected]" },
          { displayMode: "view", email: "[email protected]" },
        ];
      });
    
      describe("changeDisplayMode", () => {
        it("指定した順番の画面状態が変わる", () => {
          const changedNotifications = [
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "edit", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
          ];
          expect(
            reducer(notifications, {
              type: "changeDisplayMode",
              payload: { index: 1, displayMode: "edit" },
            })
          ).toEqual(changedNotifications);
        });
        it("指定した順番が存在しない場合、状態は変わらない", () => {
          const changedNotifications = [
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
          ];
          expect(
            reducer(notifications, {
              type: "changeDisplayMode",
              payload: { index: -1, displayMode: "edit" },
            })
          ).toEqual(changedNotifications);
        });
      });
      describe("newNotification", () => {
        it("連絡先の新規入力項目が追加される", () => {
          const addedNotifications = [
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "edit", email: "" },
          ];
          expect(
            reducer(notifications, {
              type: "newNotification",
            })
          ).toEqual(addedNotifications);
        });
      });
      describe("updateNotification", () => {
        it("指定した順番のメールアドレスが変更される", () => {
          const updatedNotifications = [
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
          ];
          expect(
            reducer(notifications, {
              type: "updateNotification",
              payload: { index: 2, value: "[email protected]" },
            })
          ).toEqual(updatedNotifications);
        });
        it("指定した順番が存在しない場合、状態は変わらない", () => {
          const nonUpdatedNotifications = [
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
            { displayMode: "view", email: "[email protected]" },
          ];
          expect(
            reducer(notifications, {
              type: "updateNotification",
              payload: { index: -1, value: "[email protected]" },
            })
          ).toEqual(nonUpdatedNotifications);
        });
      });
      // 以下省略
      });
    });
    
    이렇게reducer는 의존하지 않는 순함수이기 때문에 단독으로 테스트할 수 있다.
    논리적 관점으로만 묘사하면useStateuseReducer가 한 일은 같다.
    그러나 테스트 측면에서useReducer는 단독 테스트가 가능하고useState는 논리setStatestate에 의존하기 때문에 도저히 쓸 수 없다テストコード.
    이 차이는 매우 크다.
    useState도 강력한 Hooks라는 점은 변하지 않을 것이다.
    화면 요건에 따라 훅스를 구분해서 사용하는 게 중요하다고 생각해요.

    총결산

    useReducer 공식 문서에 설명된 대로
    state가 여러 값을 뛰어넘는 복잡한 논리가 있을 때·지난state를 바탕으로 state를 업데이트할 때
    이런 상황에서 채택함으로써 확실히 힘을 발휘할 수 있다.
    그러나 숨겨진 큰 장점은state에 의존하지 않는 구성으로 단일 테스트를 쓰기 쉽다는 것이다.
    state 디자인이 좀 복잡하면useReducer, 반대로 화면 구성이 복잡하지 않으면useState이 좋다.
    이상은 테스트 관점useReduceruseState의 차이점이다.

    좋은 웹페이지 즐겨찾기