맨 처음 hooks를 공부하면서 접하게 된 함수는 useState다. useState를 처음 사용하면서 든 생각은 너무나 편리하다는 것이었고, 이것으로 모든 문제가 해결될 거라 생각했다. 하지만 앱의 규모가 커지면서 여러 파일에서 공유해야 할 global state의 필요성이 생겼고, 이를 해결하기 위해 useReducer을 사용하게 되었다. 이 글에서는 useState는 무엇인지, useReducer은 무엇인지 알아보고 언제 무엇을 어떻게 쓸 것인지에 대해 알아보고자 한다.
먼저 리액트 공식문서에서 말하고 있는 useState에 대해 살펴보자.
const [state, setState] = useState(initialState);initialState의 값과 같다setState함수는 state를 갱신할 때 사용한다아래의 카운터 예제를 보자.
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}initialCount값을 받아오고 이 값은 count의 최초 값과 같다.
-버튼을 클릭하면 최신 count값에서 -1을 한 값으로 state가 업데이트된다.
+버튼을 클릭하면 최신 count값에서 +1을 한 값으로 state가 업데이트된다.
사실 useState는 hooks에서 가장 처음 소개되는 함수인 만큼 자주 사용되고, 그만큼 사용하기 쉽다. 하지만 이러한 useState를 사용할 때에도 주의할 점이 있다. 바로 setState는 비동기로 작동한다는 점이다.
리액트 문서를 살펴보면 이런 말이 있다.
this.props와this.state가 비동기적으로 업데이트될 수 있기 때문에 다음 state를 계산할 때 해당 값에 의존해서는 안 됩니다.
그러니 state값을 확인하고 싶을 때는 useEffect를 사용해 의존성 배열에 state 값을 넣은 후 확인하도록 하자!
리액트 공식문서에서는 useReducer에 대해서 어떻게 말하고 있는지 알아보자.
useState의 대체 함수이다(state, action) => newState의 형태로 reducer를 받고
dispatch 메서드와 짝의 형태로 state를 반환한다useState에서 살펴본 카운터 예제를 useReducer를 사용해 변경한 아래의 코드를 살펴보자.
import React, { useReducer } from 'react';
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, count: state.count + 1 };
case 'DECREASE':
return { ...state, count: state.count - 1 };
default:
throw new Error();
}
};
const Counter = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
const handleIncrease = () => {
dispatch({ type: 'INCREASE' });
};
const handleDecrease = () => {
dispatch({ type: 'DECREASE' });
};
return (
<div>
<h1>Counter with useReducer</h1>
<p>Count: {state.count}</p>
<div>
<button type="button" onClick={handleIncrease}>
+
</button>
<button type="button" onClick={handleDecrease}>
-
</button>
</div>
</div>
);
};
export default Counter;우선 counterReducer이라는 이름의 리듀서를 만들어준다. state와 action 인자로 가지고 있고, 액션 타입의 케이스에 따라 state를 업데이트해 준다.
...state는 ES6의 Destructuring assignment 이다. 매우 자주 쓰게 될 것이므로 알아두자.
컴포넌트 안에서 useReducer을 사용해 위에 정의해둔 counterReducer 리듀서와 초기 값을 받아온다. dispatch의 첫 번째 인자는 액션의 type이고, 두 번째 인자는 payload다. 액션의 타입을 체크한 후 state object를 업데이트하고, 업데이트된 state를 리턴하고 있다.
useState와 useReducer에 대해 어느 정도 알아봤다. 그러면 이러한 궁금증이 들 수 있다. 둘 다 state를 업데이트하는 건데, 언제 useState를 쓰고 언제 useReducer을 쓰면 되는 거지? 내가 일을 하면서 깨달은 한 가지 사실은, 이런 질문을 미리 걱정하지 말고 이런 질문이 자연스럽게 들 때가 오기를 기다리라는 것이다! 생각보다 그 순간은 빨리 찾아올 것이니..
리액트 문서에서는 아래와 같이 말하고 있다.
다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우나 다음 state가 이전 state에 의존적인 경우에 보통
useState보다useReducer를 선호합니다.
이번에는 useReducer vs useState in React - RWieruch 블로그에서 말하고 있는 useState와 useReducer의 사용에 대해 살펴보자.
state가 원시타입인 경우state 업데이트가 필요한 경우state가 객체인 경우 state 업데이트가 필요한 경우reducer을 사용하는게 좋다state이번 글에서는 useState와 useReducer에 대해서 알아보았다. 나는 useReducer와 Context를 함께 사용해 스토어를 따로 만들어 사용하고 있는데 이 방법에 대해서도 나중에 다뤄보고 싶다. 나는 사실 객체지향 프로그래밍 경험은 없는 데다 함수형 프로그래밍 경험도 이번이 처음이다. 하지만 사용하면 할수록 함수형 프로그래밍의 편리함을 느끼고 있다. 가장 좋은 점은 잘 정리된 문서가 항상 내 뒤에 든든하게 있다는 것 아닐까!