useReducer, useContext in React Hooks (기능과 역할) 2 - useReducer

Addie L.·2020년 6월 2일
0

UseReducer와 UseContext

목록 보기
2/2

UseReducer함수는 함수가 선언된 해당 컴포넌트 안에서만 state와 state와 관련된 로직을 관리할 수 있다.

state와 dispatch라는 두가지 속성을 사용할 수 있고
함수를 써줄 때 reducer함수initialState(상태 초기값)을 필요로한다.

const [state, dispatch] = useReducer(reducer, initialState);

컴포넌트에서 useReducer를 써서 상태와 상태와 관련된 로직을 관리해 준 예시.

const SignUpViewPr: React.FunctionComponent<RouteComponentProps> = (props) => {
  const initialState = {
    stageIdx: 1,
    errorMsg: "",
    userInput: {
      name: "",
      password: "",
      passwordCheck: "",
      email: "",
    },
  };

  //여기서의 state는 전역이다.

  const [state, dispatch] = useReducer(reducer, initialState);
  const { stageIdx, errorMsg } = state;
  const { name, password, passwordCheck, email } = state.userInput;

  const loginHandler = () => {
    props.history.push("/signin");
  };

  const nextBtnClickHandler = useCallback(() => {
    dispatch({
      type: "PROCEED_STAGE",
      stageIdx,
    });
    if (stageIdx >= 3) {
      props.history.push("/");
    }
  }, []);

  const userNameCheckHandler = useCallback((e) => {
    const { value } = e.target;

    dispatch({
      type: "NAME_CHECK",
      value,
    });
    if (!e.target.value) {
      console.log("이름을 입력해주세요");
    }
  }, []);

  const userValidateHandler = useCallback(
    (e) => {
      const IDPWCheck = /^[a-zA-Z0-9]{4,12}$/;
      const EmailCheck = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;

      if (e.target.name) {
        let inputName = e.target.name;
        const { value } = e.target;
        switch (inputName) {
          case "name":
            dispatch({
              type: "ADD_USER_INFO",
              inputName,
              value,
            });
            break;
          case "password":
            dispatch({
              type: "ADD_USER_INFO",
              inputName,
              value,
            });
            break;
          case "email":
            dispatch({
              type: "ADD_USER_INFO",
              inputName,
              value,
            });
            break;
        }
      }

      if (!e.target.value) {
        if (!name) {
          console.log("이름을 입력해주세요");
        }

        if (!email) {
          console.log("이메일을 입력해주세요");
        }

        if (!password) {
          console.log("비밀번호를 입력해주세요");
        }

        return false;
      } else {
        if (
          !validationCheckHandler(
            EmailCheck,
            email,
            "적합하지 않은 이메일 형식입니다."
          )
        ) {
          return false;
        }

        if (
          !validationCheckHandler(
            IDPWCheck,
            password,
            "패스워드는 4~12자의 영문 대소문자와 숫자로만 입력"
          )
        ) {
          return false;
        }
      }
      if (password !== passwordCheck) {
        dispatch({
          type: "ADD_ERROR_MSG",
          message: "비밀번호가 일치하지 않습니다.",
        });
        return false;
      }
    },
    [name, email, password]
  );

  const validationCheckHandler = useCallback(
    (expression: RegExp, value: string, message: string) => {
      if (expression.test(value)) {
        return true;
      }
      dispatch({
        type: "ADD_ERROR_MSG",
        message,
      });
    },
    []
  );

  //props를 객체로 만들어서 index에 있는 걸 꺼내쓸수있게

  const txtProps = {
    headerTxt: {
      1: "회원가입",
      2: `${name}님 환영합니다.`,
    },
    descTxt: {
      1: "아래 정보를 입력하고 회원가입을 진행하세요.",
      2: "아래 정보를 입력하고 회원가입을 완료하세요.",
    },
  };

  return (
    <UserDispatch.Provider value={dispatch}>
      <SignupViewWrapper>
        <SignUpCont
          match={props.match}
          matchId={stageIdx}
          headerTxt={txtProps.headerTxt[stageIdx]}
          descTxt={txtProps.descTxt[stageIdx]}
          userNameCheckHandler={(e) => userNameCheckHandler(e)}
          userValidateHandler={(e) => userValidateHandler(e)}
        />
        <ErrorMsg hasError={errorMsg}>{errorMsg}</ErrorMsg>
        <Button
          onClick={nextBtnClickHandler}
          buttonName={"PRIMARY"}
          buttonText={"다음"}
        />
        <LoginCheckCont>
          이미 계정이 있나요?
          <LoginTxt
            onClick={() => {
              loginHandler();
            }}
          >
            로그인
          </LoginTxt>
        </LoginCheckCont>
        {stageIdx === 1 && (
          <CopyrightBox>
            <OwnerInfo>CherGround Inc. All rights reserved.</OwnerInfo>
          </CopyrightBox>
        )}
      </SignupViewWrapper>
    </UserDispatch.Provider>
  );
};

const SignupViewWrapper = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
`;

const ErrorMsg = styled.div`
  display: ${(props) => (props.hasError ? "block" : "none")};
  width: 420px;
  height: fit-content;
  font-size: 14px;
  color: #fd0000;
  font-family: NanumSquare;
  font-weight: 400;
  margin-top: 12px;
  margin-bottom: 33px;
`;

const LoginCheckCont = styled.div`
  width: 420px;
  height: fit-content;
  font-family: NanumSquare;
  font-weight: 400;
  color: #1f263e;
`;

const LoginTxt = styled.span`
  margin-left: 8px;
  color: #1e1e1e;
  text-decoration: underline;
`;

const CopyrightBox = styled.div`
  width: 100%;
  height: 52px;
  border-top: 1px solid #eeeeee;
  background-color: #ffffff;
  margin-top: auto;
`;

const OwnerInfo = styled.p`
  font-family: NanumSquare;
  font-weight: 300;
  font-size: 12px;
  color: #7a7f8e;
  margin: 20px 0 19px 128px;
`;

export default withRouter(SignUpViewPr);

reducer

const reducer = (state, action) => {
  switch (action.type) {
    case "PROCEED_STAGE":
      return {
        ...state,
        stageIdx: ++action.stageIdx,
      };

    case "NAME_CHECK":
      return {
        ...state,
        userInput: {
          ...state.userInput,
          name: action.value,
        },
      };

    case "ADD_USER_INFO":
      return {
        ...state,
        userInput: {
          ...state.userInput,
          [action.inputName]: action.value,
        },
      };

    case "ADD_ERROR_MSG":
      return {
        ...state,
        errorMsg: action.message,
      };

    default:
      return state;
  }
};

export default reducer;

0개의 댓글