useEffect 자세히 이해하기 3- useEffect와 useReducer

김명성·2022년 5월 11일
0

REACT

목록 보기
25/32
post-custom-banner

useReducer & useEffect

// Reducer 함수를 컴포넌트 함수 바깥으로 꺼낸 이유는
// 리듀서 함수 내부에서는 컴포넌트 함수 내부에서 생성된
// 어떠한 데이터도 필요하지 않기 때문이다.
const emailReducer = (state, action) => {
  if (action.type === 'USER_INPUT') {
    // 여기서의 action.val은 event.target.value이다.
    return { value: action.val, isValid: action.val.includes('@') };
  }
  if (action.type === 'INPUT_BLUR') {
    // 'INPUT_BLUR'가 실행될때에는
    // state의 값은 바뀔 필요가 없기에
    // value에 가장 최신의 snapshot을 사용한다.
    // isValid에서 가장 최신의 snapshot에 대해 validate을 진행한다.
    return { value: state.value, isValid: state.value.includes('@') };
  }
  return {
    value: '',
    isValid: false,
  };
};

const passwordReducer = (state,action) => {
  if (action.type === 'USER_INPUT'){
    
    return {value: action.val, isValid: action.val.trim().length > 6}
  }
  if (action.type === 'INPUT_BLUR') {
    // 'INPUT_BLUR'가 실행될때에는
    // state의 값은 바뀔 필요가 없기에
    // value에 가장 최신의 snapshot을 사용한다.
    // isValid에서 가장 최신의 snapshot에 대해 validate을 진행한다.
    return {value: state.value, isValid: state.value.trim().length > 6}
  }
  return {
    value: '',
    isValid: false,
  }
}


const Login = (props) => {

  const [formIsValid, setFormIsValid] = useState(false);
  // [state snapshot , action dispatch(reducerFn - 최신 state snapshot을 가져옴)]
  // useReducer(reducerFn, initalState, initFn)
  // reducerFn(state,action)
  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: '',
    isValid: null,
  });

  const [passwordState, dispatchPassword] = useReducer(passwordReducer,{
    value: '',
    isValid: null
  })

  // useEffect를 정확하게 동작시키기 위해 
  // Destructuring으로 isValid 값만 추출한다.
  const {isValid: emailIsValid} = emailState;
  const {isValid: passwordIsValid} = passwordState;
   



  useEffect(()=>{
    const identifier = setTimeout(()=> {
      console.log('useEffect - form validation')
      setFormIsValid(
        emailIsValid && passwordIsValid
      )
    },500)
    return () => {
      console.log('clean up function 2번째 side effect실행 전 또는 컴포넌트 제거 전 실행')
      clearTimeout(identifier);
    }
    //의존성 배열에 desctructuring으로 isValid 값을 빼내와서
    //value 값이 바뀔때마다 계속 실행되는 것을 막는다.
  },[emailIsValid,passwordIsValid])


  const emailChangeHandler = (event) => {
    dispatchEmail({ type: 'USER_INPUT', val: event.target.value });
  };

  const passwordChangeHandler = (event) => {
    dispatchPassword({type: 'USER_INPUT', val:event.target.value})
  };

  const validateEmailHandler = () => {
    dispatchEmail({ type: 'INPUT_BLUR' });
  };

  const validatePasswordHandler = () => {
    dispatchPassword({type:'INPUT_BLUR'})
  };

  const submitHandler = (event) => {
    event.preventDefault();
    props.onLogin(emailState.value, passwordState.value);
  };

  return (
    ...
  );
};

export default Login;

위 예제에서 Destructuring을 하지 않고 emailState.isValid와 같은 프로퍼티 키/값 형식을 의존성 배열에 추가해도 정상적으로 작동한다.
핵심은 destructuring을 사용한다는 것이 아니라, 전체 개체 대신 특정 속성을 useEffect의 의존성 배열로 전달한다는 것이다.

아래의 예시도 정상적으로 작동한다.

  useEffect(()=>{
    const identifier = setTimeout(()=> {
      setFormIsValid(
        emailIsValid && passwordIsValid
      )
    },500)
    return () => {
      clearTimeout(identifier);
    }
  },[emailState.isValid,passwordState.isValid])
post-custom-banner

0개의 댓글