// 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])