React.js userReducer

강정우·2023년 1월 2일
1

react.js

목록 보기
17/45
post-thumbnail
post-custom-banner

userReducer

  • state 관리를 도와주면 useState와 비슷하지만 더 많은 기능이 있고 더 복잡한 state에 유용하다.
  • 하지만 항상 기능이 더 많다고 해서 더 좋은건 아니다! 사용이 더 복잡하기 때문에 따라서 useState를 고집하되 필요한 경우에만 useReducer를 사용하는 것을 추천한다.

언제?

  • useState를 사용하면 너무 번거로운 경우,
    너무 많은 일들을 처리해야 하는 경우,
    관련 state 스냅샷들이 서로 독립적이고 한큐에 업데이트가 잘 안 될 때

  • 예를들어 많은 state가 같이 있거나 여러 state가 한번에 바뀌거나 sate끼리 관련된 경우를 말한다.

  • 현재 이 상황에서는 불필요한 state가 너무 많다. 자세히 보면 email입력, 검증을 하나로 묶을 수 있을것 같고 pw입력, 검증을 하나로 묶을 수 있을 것 같다.

  • 그리고 코드를 보면 사실 앞서 포스팅한것 처럼 state를 업뎃을 할땐 이렇게 스냅샷에만 의존하면 버그가 발생하여 함수사용을 권장한다 했었다. 하지만 여기선 그렇게 할 수 없다. 왜냐하면 다음 state 업데이트가 동일한 state의 이전 state의 스냅삿에 의존하는 경우에만 가능하기 때문이다. 하지만 위 코드에서는 서로 다른 두개의 state의 스냅샷에 의존하고있기 때문에 물가능하다.

    즉, 위와같이 2개의 서로 다른 state를 한큐에 업뎃할 때 사용하면 좋다.
    또는 state를 기반으로 state를 업뎃하는 경우에

  • 그럼 이해를 돕기 위해 위 코드는 맞는 코드인가?
    틀렸다. 왜냐하면 위의 state들의 사진을 보면 알 수 있듯 validate함수와 입력된값들은 서로다른 2개의 state를 갖고있기 때문이다!
  • 그래서 일반적인 state 업뎃함수를 사용할 수 없다. 왜냐? state를 유효성 state를 써야하는데 ented state를 사용해버리기 때문이다.
    그래서 사실 state를 하나의 객체로 만들어서 관리하는 법도 있지만 state가 더 복잡하고 커지기 때문에 이럴 때 useReducer을 사용하면 좋다.
  • useReduer는 조금 더 효과적으로 실행되어야 할 때 쓰면 좋다고 배웠다
    • state value들이 많이 있을 때
    • 업데이트 로직이 많이 복잡하거나 할 때
    • 개별적으로 관리되며 서로 연관되어있는 state일 때

어떻게?

const [state, dispatchFn] = useReducer(reducerFn, initialState, initFn);
  • useReducer도 useState처럼 2개의 배열을 반환한다.

  • state : 최신 state 스냅샷

  • dispatchFn : 위의 state 스냅샷을 업데이트할 수 있는 함수 (액션을 디스패치(최신화)함)

  • reducerFn : 액션을 소모하는 함수, 최신 state 스냅샷과 디스패치된 액션을 자동으로 가져오는 함수
    reducerFn 은 컴포넌트함수 외부에 선언되는데 그 이유은 컴포넌트 함수 내부에서 만들어진 어떤 데이터도 필요하지 않기 때문이다. 그리고 어차피 react가 알아서 보내주기 때문에

  • initialState : 초기값

  • initFn : 초기 state를 설정하기위해 실행해야하는 함수 (http request의 결과)

예시

  1. JSX 작성
  • value와 onChange, onBlur prop값 + isValid을 설정.
    value : 설정한 값을 대입하기위해서
    onChange : 변경사항을 가져와서 로직을 돌리려고
    onBlue : input 태그의 focus가 빠졌을 때 값을 가지고 로직을 돌리려고
    isValid : 최종 로직을 거처 나온 결과값으로 만약 false면 invalid 속성을 활성화 함.
  1. reducer 선언
  • state는 초기화 구문을 기반으로 최초값을 가짐
  • dispatch 함수는 state를 조작할 수 있도록 도와주는 action을 설정함.
  • reducer함수는 action과 state를 기반으로 state값을 로직을 거쳐 return해줌
  1. Trigger 함수들과 dispatchFn 작성
  • 앞서 언급했듯 dispatch 함수들은 action을 조작하기위해 선언. 즉, action객체의 prop값임.
    그니까 원래 우리가 했던것은 state를 기반으로 state 업뎃함수를 사용해서 prev~~ 매개변수로 state 최신값을 가져와서 업뎃을 했는데 2개의 state를 사용해야하다 보니 일반적인 state로는 해결이 불가능해서 Reducre의 dispatch 함수를 사용하여 action을 추가하여 state를 최신화하는 것이다.
  • setFormIsValid는 pw를 먼저 작성하는 사용자가 있을 수 있으므로 최종 보내기 전에 state를 가져와 email과 pw를 검사해주는 코드이다.
  1. reducerFn 작성
  • 현재 event로 들어온 입력값을 state로 자동으로 유지해주고 dispatchFn으로 갖고온 action 객체를 자동으로 가져온다. 이는 React가 알아서 자동으로 가져온다.
  1. Trigger 함수 수정
  • 왜 수정해주냐 앞서 배운것처럼 현재 2개의 state(email, pw)를 참조해버렸기 때문에 State가 변경될 대마다 값을 가져오는 Effect 방식으로 처리해준다.
  • 추가로 값을 우와같이 수정할 수도 있는데 우선 선언부에 alias를 사용하여 객체의 prop 값을 선언해주고 이를 사용하여 밑에 Effect함수가 너무 빈번하게 일어나지 않도록 정확히 유효성 측면의 boolean 값에서만 일어나도록 설정해주었다. 또한 의존성과 함수 내부의 변수는 일치해야하므로 똑같이 작성해주었다.

예제 코드

useInput.js(feat.useState)

import { useState } from "react";

const useInput = (validateValueFn) => {
  const [enteredValue, setEnteredValue] = useState("");
  const [isTouched, setIsTouched] = useState(false);

  const valueIsvalid = validateValueFn(enteredValue);
  const hasError = !valueIsvalid && isTouched;

  const valueChangeTrigger = (event) => {
    setEnteredValue(event.target.value);
  };
  const inputBlurTrigger = () => {
    setIsTouched(true);
  };
  const reset = () => {
    setEnteredValue("");
    setIsTouched(false);
  };
  return {
    value: enteredValue,
    isValid: valueIsvalid,
    hasError,
    valueChangeTrigger,
    inputBlurTrigger,
    reset,
  };
};

export default useInput;

useInput.js(feat.useReducer)

import { useReducer } from "react";

const initialInputState = {
    value:"",
    isTouched:false
}

const inputStateReducer = (state, action) => {
    if(action.type==="INPUT"){
        return{ value: action.value, isTouched: state.isTouched }
    } else if (action.type==="BLUR") {
        return{ isTouched: true, value: state.value }
    } else if (action.type==="RESET") {
        return { isTouched: false, value: "" }
    } else {
        console.log("something is wrong in user-hook")
    }
    return {
        // new state snap shot
        // 하지만 위의 조건을 충족시키지 못 했다면 초기화 구문임
        initialInputState
    };
}

const useInput = () => {
    const [inputState, dispatch] = useReducer(inputStateReducer, initialInputState);

    const valueIsvalid = inputState.value.trim() !== "";
    const hasError = !valueIsvalid && inputState.isTouched;

    const valueChangeTrigger = (event) => {
        dispatch({type:"INPUT", value:event.target.value});
    };
    const inputBlurTrigger = (event) => {
        dispatch({type:"BLUR"});
    };
    const reset = () => {
        dispatch({type:"RESET"})
    };
    return {
        value: inputState.value,
        isValid: valueIsvalid,
        hasError,
        valueChangeTrigger,
        inputBlurTrigger,
        reset,
    };
};

export default useInput;
profile
智(지)! 德(덕)! 體(체)!
post-custom-banner

0개의 댓글