useReducer는 React에 내장된 Hook
이며,
useState와 비슷하게 state 관리를 도와주지만, 더 복잡한 state에 특히 유용하게 쓰인다
const [state, dispatchFn] = useReducer(reducerFn, initialState, initFn)
state
: 최신 state 스냅샷
dispatchFn
: state를 update할 수 있는 함수(새로운 state값을 설정하는 대신, 액션을 디스패치 한다.)
reducerFn
: 액션을 소비하는 함수. 최신 state 스냅샷을 자동으로 가져온다. 또한, 디스패치된 액션을 가져온다. (새 액션이 디스패치될 때 마다 이 함수가 호출된다.)
initialState
: 초기 state
initFn
: 초기 state를 설정하는 함수
가끔, 복잡한 state
를 가질 경우가 있다.
예를 들어, 다수의 state들이 함께 속해 있는 경우 또는 같이 바뀌거나 서로 연관된 경우
,
다른 state를 기반으로 하는 state를 업데이트 할 때
등이 있다.
이 경우에 useState()는 다수의 state들을 관리하기에 부족할 수 있다.
(코드가 bad, inefficient해질 수 있다.🤮)
이때 사용가능한 것이, useReducer()
이다.
즉, 더 복잡한 state들의 관리에 유용하게 쓰인다.
✅
useReducer
를 사용하면, state를 처리하는 로직 부분을 컴포넌트에서분리
시켜 작성할 수 있다.즉, 컴포넌트를 최적화 시킬 수 있다는 의미이다.
다음의 예시 코드를 보자.
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState('');
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState('');
const [passwordIsValid, setPasswordIsValid] = useState();
// email 인풋 필드에 값이 변경 시 호출하는 핸들러 함수
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
};
// password 인풋 필드에 값이 변경 시 호출하는 핸들러 함수
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
};
// email 인풋 필드를 올바르게 입력했는지 검사하는 핸들러 함수
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes('@'));
};
// password 인풋 필드를 올바르게 입력했는지 검사하는 핸들러 함수
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
// 컴포넌트 외부
const emailReducer = (state, action) => {
if (action.type === "USER_INPUT") {
return { value: action.val, isValid: action.val.includes("@") };
}
if (action.type === "INPUT_BLUR") {
return { value: state.value, isValid: state.value.includes("@") }; // 이전에 가졌던 state.value가 value가 된다.
}
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") {
return { value: state.value, isValid: state.value.trim().length > 6 };
}
return { value: "", isValid: false };
};
// Login 컴포넌트
const Login = (props) => {
const [emailState, dispatchEmail] = useReducer(emailReducer, {
value: "",
isValid: undefined,
});
const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
value: "",
isValid: undefined,
});
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 = () => {
dispatchEmail({ type: "INPUT_BLUR" });
};
useReducer
를 사용했을 때reducer 함수
를 "Login 컴포넌트 외부로 뺀" 모습을 볼 수 있다.이것이 가능한 이유는, reducer 함수 내부에서는 컴포넌트 함수 내부에서 만들어진 어떤 데이터도 필요하지 않기 때문이다.
따라서,
reducer 함수는 컴포넌트 함수의 범위 밖에서 만들어질 수 있다.
(reducer 함수 내부에서 요청되고 사용되는 모든 데이터는 리액트가 이 함수를 실행할 때 자동으로 전달된다.)
공통적으로 useReducer
와 useState
는 모두 state를 처리하는 Hook
이다.
그렇다면, useReducer
와 useState
중 언제 어느것을 써야하는지 판단 할 수 있을까?
- useState
- 주요 state 관리 도구이다.
- 개별 state 및 데이터들을 다루기에 적합하다.
- 간단한 state에 적합하다.
- state의 업데이트가 쉽고, 몇 종류 안되는 경우에 적합하다.
- useReducer
- state로서의 객체가 있는 경우
- 복잡한 state가 있는 경우
- 관리해야 할 state가 여러개일 경우
- 다른 연관된 state 조각들로 구성된 어떤 state를 관리해야 하는 경우