useReducer는 함수로 보낸 액션을 기반으로 해서 state 상태를 최신의 상태로 바꾸어 줍니다.
const [state, dispatch] = useReducer(reducer, initialState);
저장되는 값은 [state, dispatch]가 있습니다. state는 initialState로 처음 설정되는 값입니다.
dispatch는 함수입니다. 특정한 액션을 reducer 파라미터로 넘깁니다. 그러면 reducer은 state를 업데이트 시킵니다.
reducer는 dispatch로 받은 상태를 이용해 state상태를 최신 버전으로 만들어 주는 작업을 수행합니다.
initialState은 말 그래도 초기값을 설정하는 데에 사용합니다.
initialState은 value, object도 가능합니다.
카운트를 위해 초기값을 설정한다면 다음과 같이 가능합니다.
const [state, dispatch] = useReducer(reducer, 0);
const [state, dispatch] = useReducer(reducer, {count : 0});
아무래도 객체를 다루는 경우가 많음으로 두 번째 경우를 많이 사용합니다.
그러면 reducer은 어떻게 작동할까요??
function reducer(state, action){ }
위와 같은 형태로 작성합니다. 첫 파라미터는 현재 state입니다. 두 번째 파라미터는 dispatch를 통해 넘긴 action을 받아옵니다. action에 따라 함수를 실행하고 update된 state를 반환시킵니다.
function reducer (state, action) { switch(action.type){ case "Increment" : return {count : state.count +1} case "Decrement": return {count : state.count -1} default: return state; } } function Counter() { const [state, dispatch] = useReducer(reducer, {count : 0}); const onIncrease = () => { dispatch({type : "Increment"}); }; const onDecrease = () => { dispatch({type : "Decrement"}); };
Counter기능을 수행한다면 위와 같이 만들 수 있습니다.
여기서 주의할점은 객체를 통해 초기화했음으로 반환하는 것 역시 객체형태로 해줘야합니다.
코드를 더 업데이트 시켜봅시다.
const ACTIONS = { INCREMENT : 'increment', DECREMENT :'decrement' } function reducer (state, action) { console.log(state); switch(action.type){ case ACTIONS.INCREMENT : return {count : state.count + 1} case ACTIONS.DECREMENT: return {count : state.count + -1} default: return state; } } function Counter() { const [state, dispatch] = useReducer(reducer, {count : 0}); console.log(state); const onIncrease = () => { dispatch({type : ACTIONS.INCREMENT}); }; const onDecrease = () => { dispatch({type : ACTIONS.DECREMENT}); };
"Increment" "Decrement" <== 처럼 하드코딩을 하고있습니다. 전역변수로 action을 선언해서 더 안전한 코드를 작성했습니다.
useReducer은 useState와 비슷하게 새로운 상태를 만들때 사용합니다. 다른 점은 복잡한 컴포넌트나 관리해야할 state가 많을 때 유용합니다.