[SeSACx코딩온] useReducer
useReducer를 이용한 상태 관리
1. useReducer란?
useReducer
는 useState
보다 더 복잡한 상태 관리 로직을 처리하기 위한 React 훅입니다. 특히 상태가 여러 하위 요소로 구성되거나, 상태 변경 로직이 복잡한 경우에 사용됩니다. 이 훅은 컴포넌트 외부에 상태 업데이트 로직을 분리하여 코드의 가독성과 유지보수성을 높일 수 있습니다.
2. 설명
1) 초기 상태와 reducer 함수 정의
// #1. 초기 상태값 정의
const initState = { value: 0 }; // 초기 상태 값
// #2. reducer 함수 정의
// - 이 함수는 현재 상태와 액션을 받아 새로운 상태를 반환
const reducer = (prevState, action) => {
console.log('prevState >>> ', prevState); // {value: 0}
// action
console.log('action >>> ', action); // {type: 'INCREMENT'}
console.log('action.type >>> ', action.type); // INCREMENT
switch (action.type) {
case 'INCREMENT':
return { value: prevState.value + 1 };
case 'DECREMENT':
return { value: prevState.value - 1 };
case 'RESET':
return initState;
default:
return { value: prevState.value }
}
}
- 초기 상태 정의
initState
는 상태의 초기값을 정의한 객체입니다. 여기서는value
라는 상태 값을 0으로 초기화하고 있습니다.
- reducer 함수
reducer
함수는 현재 상태(prevState
)와 액션(action
)을 받아 새로운 상태를 반환하는 순수 함수입니다. 액션은 상태를 변경하기 위한 명령을 포함하고 있으며, 이 함수는 액션의 종류에 따라 상태를 업데이트합니다.switch
문을 통해 액션의 타입(INCREMENT
,DECREMENT
,RESET
)에 따라 상태를 변경합니다.
2) UseReducerEx
컴포넌트
import React, { useReducer } from 'react';
export default function UseReducerEx() {
// #3. useReducer 훅 사용.
const [state, dispatch] = useReducer(reducer, initState);
// state: 현재 상태
// dispatch: 액션을 발생 시키는 함수 (state가 어떻게 변경되어야 하는지에 대한 힌트 제공)
// reducer: state를 업데이트 하는 함수
console.log('state >>> ', state); // {value: 0}
// #4. 액션 핸들러 함수 정의
// - 이 함수들은 'dispatch'를 호출하여 액션을 발생 시킴
const increment = () => dispatch({ type: 'INCREMENT' });
const decrement = () => dispatch({ type: 'DECREMENT' });
const reset = () => dispatch({ type: 'RESET' });
return (
<div>
<h1>UseReducerEx</h1>
<h2>{state.value}</h2>
{/* #5. 컴포넌트 랜더링 */}
<button onClick={increment}>PLUS</button>
<button onClick={decrement}>MINUS</button>
<button onClick={reset}>Reset</button>
</div>
);
}
3) UseReducerEx
컴포넌트 분석
useReducer
훅을 사용하여 상태 관리를 수행합니다. useReducer
는 두 가지 요소를 반환하는데, 첫 번째는 현재 상태(state
)이고, 두 번째는 상태를 업데이트하기 위한 함수인 dispatch
입니다.
- useReducer 훅 사용
useReducer
는reducer
함수와 초기 상태값(initState
)을 인자로 받아, 현재 상태와 상태를 변경할 수 있는dispatch
함수를 반환합니다.state
는 현재의 상태를 나타내며, 이 상태 값은 컴포넌트가 리렌더링될 때마다 변경된 상태를 반영합니다. 예를 들어, 여기서는value
가 상태로 정의되어 있으며, 초기값은0
입니다.dispatch
는 상태를 변경하기 위해 호출하는 함수로,reducer
함수에 특정 액션을 전달하여 새로운 상태를 계산하도록 합니다.
- 액션 핸들러 함수 정의
increment
,decrement
,reset
함수는 각각dispatch
를 호출하여 상태를 변경하는 액션을 발생시킵니다.increment
함수는dispatch({ type: 'INCREMENT' })
를 호출하여value
를 1 증가시키는 액션을 전달합니다.decrement
함수는dispatch({ type: 'DECREMENT' })
를 호출하여value
를 1 감소시키는 액션을 전달합니다.reset
함수는dispatch({ type: 'RESET' })
를 호출하여 상태를 초기값으로 되돌리는 액션을 전달합니다.
- 액션들은
dispatch
에 의해reducer
함수로 전달되며, 해당 액션의 타입에 따라reducer
함수가 새로운 상태를 계산하고 반환합니다.
- 컴포넌트 렌더링
- 컴포넌트는
state.value
를 화면에 출력하며, 세 개의 버튼(PLUS
,MINUS
,Reset
)을 제공합니다. 사용자가 버튼을 클릭하면 해당 액션 핸들러가 호출되고, 이에 따라 상태가 업데이트됩니다. - 예를 들어, 사용자가
PLUS
버튼을 클릭하면increment
함수가 호출되어dispatch
를 통해INCREMENT
액션이 발생하고, 그 결과value
가 1 증가합니다. 상태가 변경되면 컴포넌트는 리렌더링되어 화면에 새로운 값이 표시됩니다.
- 컴포넌트는
4) 상태 변경 과정 요약
(1) 사용자가 버튼(PLUS
, MINUS
, Reset
)을 클릭합니다.
(2) 해당 버튼의 클릭 이벤트가 발생하면, 각 액션 핸들러 함수(increment
, decrement
, reset
)가 실행됩니다.
(3) 핸들러 함수가 dispatch
를 호출하여 reducer
함수에 액션(type
)을 전달합니다.
(4) reducer
함수가 현재 상태와 전달된 액션을 바탕으로 새로운 상태를 계산하여 반환합니다.
(5) 컴포넌트가 리렌더링되고, 화면에 새로운 상태값이 출력됩니다.
3. useState vs. useReducer
React에서 useState
와 useReducer
는 모두 컴포넌트의 상태를 관리하기 위한 훅입니다. 그러나 이 두 훅은 상태의 복잡도와 상태 변경 로직에 따라 각각 다른 상황에 적합합니다.
1) useState
useState
는 간단한 상태를 관리하는 데 적합한 훅입니다. 예를 들어, 숫자, 문자열, 불리언 등과 같은 단일 값이나, 간단한 객체 상태를 관리할 때 유용합니다.-
상태가 하나 또는 두 개 정도로 단순한 경우, 그리고 상태 변경 로직이 복잡하지 않은 경우
useState
를 사용하는 것이 더 직관적이고 간편합니다.import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = () => setCount(count + 1); const decrement = () => setCount(count - 1); return ( <div> <h2>Count: {count}</h2> <button onClick={increment}>Increase</button> <button onClick={decrement}>Decrease</button> </div> ); }
- 단순한 카운터 기능을 구현한 것입니다. 상태(
count
)는 단일 숫자 값이고, 상태 변경 로직도 간단하기 때문에useState
를 사용하는 것이 적절합니다.
- 단순한 카운터 기능을 구현한 것입니다. 상태(
- 장점
- 사용이 간편하고, 상태 변경 로직이 간단한 경우 직관적입니다.
- 코드를 간결하게 유지할 수 있습니다.
- 단점
- 상태가 복잡해지거나, 여러 개의 상태가 상호 연관될 때 코드가 비대해지고 관리하기 어려워질 수 있습니다.
2) useReducer
useReducer
는 상태가 복잡하거나, 여러 단계의 상태 변경 로직이 필요한 경우 적합한 훅입니다. 상태가 객체나 배열로 구성되어 있거나, 상태 변경 로직이 여러 가지로 분기되는 경우,useReducer
를 사용하면 로직을 명확하고 구조화된 방식으로 관리할 수 있습니다.-
useReducer
는 상태 관리 로직을 컴포넌트 외부로 분리할 수 있게 해주며,reducer
함수 내에서 상태 변경 로직을 집중적으로 관리할 수 있습니다.import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; case 'DECREMENT': return { count: state.count - 1 }; case 'RESET': return { count: 0 }; default: return state; } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <h2>Count: {state.count}</h2> <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increase</button> <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrease</button> <button onClick={() => dispatch({ type: 'RESET' })}>Reset</button> </div> ); }
- 카운터의 상태를 관리하기 위해
useReducer
를 사용하고 있습니다.reducer
함수는 다양한 액션 타입(INCREMENT
,DECREMENT
,RESET
)에 따라 상태를 업데이트하는 로직을 관리합니다. 상태 변경 로직이 분명하게 구조화되어 있고, 상태가 복잡한 경우에도useReducer
를 사용하면 코드가 명확해집니다.
- 카운터의 상태를 관리하기 위해
- 장점
- 상태가 복잡하거나, 상태 변경 로직이 여러 단계로 이루어진 경우에 더 구조적인 접근을 할 수 있습니다.
- 상태 관리 로직을
reducer
함수로 분리하여, 컴포넌트 외부에서 관리할 수 있어 코드의 재사용성과 유지보수성이 향상됩니다.
- 단점
useState
에 비해 초기 설정이 복잡할 수 있으며, 상태가 단순한 경우에는 오히려 불필요하게 복잡한 코드를 작성하게 될 수 있습니다.
3) 상황에 따른 선택 기준
- 상태의 복잡도
- 상태가 단순하고 변경 로직이 간단한 경우
useState
가 적합합니다. - 상태가 복잡하고, 여러 단계의 로직이 필요하거나 상태가 여러 개의 값으로 구성되어 있을 때는
useReducer
가 적합합니다.
- 상태가 단순하고 변경 로직이 간단한 경우
- 코드의 가독성
useState
는 간단한 상태 관리에 적합하며, 코드가 직관적이고 가독성이 좋습니다.useReducer
는 복잡한 상태 로직을 명확하게 분리하여, 가독성과 유지보수성을 높일 수 있습니다.
- 상태 변경의 예측 가능성
useReducer
를 사용하면 모든 상태 변경 로직이reducer
함수에 집중되기 때문에, 상태 변화의 흐름을 더 명확하게 추적할 수 있습니다.useState
는 간단한 상태 변경에 적합하지만, 상태 변경 로직이 분산될 수 있습니다.