다음과 같은 앱의 validity를 확인할 때, useState를 사용하면 문제점이 발생 할 수도있다. 아래의 다섯가지 상태에 의존한다. enteredEmail은 입력받은 값이고 emailIsValid는 이메일의 유효성을 검증하는 상태이다.
const [enteredEmail, setEnteredEmail] = useState('');
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState('');
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
그런데 이메일이 변했을 때, 유효성을 검증하려면 password state의 정보도 필요하기 때문에 이전상태에 의존한 업데이트를 해야한다. 이것은 문제를 일으킬 수도 있다. emailIsValid
는 enteredEmail
에 의존하기 때문이다.
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
setFormIsValid(
event.target.value.includes('@') && enteredPassword.trim().length > 6
);
};
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes('@'));
};
그래서 useReducer
이라는 훅을 사용할 것인데, useReducer
복수의 상태를 한번에 업데이트 할 수 있다.
import { useReducer } from "react";
초기 상태를 넣어준다.
const [emailState, dispatchEmail] = useReducer(emailReducer, {
value: "",
isValid: null,
});
dispatch
를 넣어준다.const emailChangeHandler = (event) => {
dispatchEmail({ type: "USER_INPUT", value: event.target.value });
setFormIsValid(event.target.value.includes("@") && passwordState.isValid);
};
reducerFn
을 정의해 준다.const emailReducer = (state, action) => {
if (action.type === "USER_INPUT") {
return { value: action.value, isValid: action.value.includes("@") };
}
if (action.type === 'INPUT_BLUR') {
return {value: state.value, isValid: state.isValid}
}
return { value: "", isValid: false };
};
그렇다면 이제
emailState.value
로 값을,emailState.isValid
로 유효성을 검증 할 수 있다.
useEffect(() => {
const identifier = setTimeout(() => {
console.log("Checking form validity!");
setFormIsValid(emailState.isValid && passwordState.isValid);
}, 500);
return () => {
console.log("CLEANUP");
clearTimeout(identifier);
};
}, [emailState, passwordState]);
이전에
useEffect
를 이용해서 유효성 검증에 너무 많은 요청을 보내지 않도록 했었다. 그런데 예를들어, 패스워드를 6글자 이상 입력해야 하지만 7글자에서 더 추가한다고 해서 유효성이 바뀌지 않는다.
따라서 상태에 따라서 유효성을 검증할 것이 아닌, 유효성이 변할 때만 useEffect
를 사용하도록 하자.
다음과 같이 구조분해를 먼저 사용해서 emailState
안에 있는 isValid
라는 변수를 emailIsValid
라고 쓰기로 한다.
const { isValid: emailIsValid } = emailState;
const { isValid: passwordIsValid } = passwordState;
useEffect(() => {
const identifier = setTimeout(() => {
console.log("Checking form validity!");
setFormIsValid(emailState.isValid && passwordState.isValid);
}, 500);
return () => {
console.log("CLEANUP");
clearTimeout(identifier);
};
}, [emailIsValid, passwordIsValid]);
useEffect(()=>{},[emailState.isValid, passwordState.isValid])
로 쓰면 안되나..?
이전 강의에서 우리는 useEffect()에 객체 속성을 종속성으로 추가하기 위해 dstructuring을 사용했습니다.
const { someProperty } = someObject;
useEffect(() => {
// code that only uses someProperty ...
}, [someProperty]);
이것은 매우 일반적인 패턴 및 접근 방식이며, 이것이 제가 일반적으로 이 방식을 사용하는 이유이며 여기서 보여드리는 이유입니다(코스 내내 계속 사용할 것입니다).
핵심은 우리가 destructuring
을 사용한다는 것이 아니라, 전체 개체 대신 특정 속성을 종속성으로 전달한다는 것입니다.
우리는 이와 같이 코드를 작성할 수도 있으며 같은 방식으로 작동합니다.
useEffect(() => {
// code that only uses someProperty ...
}, [someObject.someProperty]);
이것은 잘 작동합니다!
하지만 여러분은 이 코드 사용을 피해야 합니다:
useEffect(() => {
// code that only uses someProperty ...
}, [someObject]);
왜 그럴까요?
왜냐하면 effect
함수는 someObject
가 변경될 때마다 재실행되기 때문이죠 - 단일 속성이 아닙니다 (someProperty
위의 예에서)