📌 useReducer()
: 복잡한 상태(state) 로직을 처리하는 상태 관리 훅
- useState와 유사하지만, 상태(state) 업데이트 로직을 더 명확하게 분리하고 다양한 상태(state) 변화를 처리한다.
- 현재 상태와 액션을 받아 새로운 상태를 반환하는 리듀서 함수를 사용한다.
➡︎ 상태 변화는 액션에 의해 일어나며, 이 액션은 리듀서 함수에 전달된다.
- useReducer에서 사용하는 리듀서 함수는 순수해야 한다.
➡︎ 사이드 이펙트 코드가 들어가면 안된다. - 사용
- 상태(state) 변화가 복잡하고, 여러 가지 액션에 따라 다양한 상태 변경이 필요할 때
- 상태(state)와 관련된 로직을 분리하여 관리하고 싶을 때
- 리덕스(Redux)와 같은 상태 관리 패턴을 작은 컴포넌트 내에서 구현하고 싶을 때
⏩ useState vs useReducer
특징 | useState | useReducer |
사용 목적 | 단순한 상태 관리 | 복잡한 상태 로직 관리, 여러 상태를 조작해야 할 때 |
제공 기능 | 하나의 상태 값과 상태를 변경할 수 있는 함수 제공 | 현재 상태와 dispatch 함수를 반환 |
상태 업데이트 방식 | 간단한 값 변경 | 상태 업데이트 로직을 별도의 리듀서 함수로 분리 |
사용 상황 | - 단일 변수 상태 관리 - 간단한 값 변경 |
- 여러 상태 관리 - 상태 변경 로직이 복잡하거나 다양할 때 - 상태를 업데이트하는 코드 재사용이 필요한 경우 |
코드 가독성 및 유지보수성 |
간단한 상태 관리에서는 가독성이 좋음 | 리듀서로 로직을 분리함으로써 복잡한 로직 관리 시 가독성과 유지보수성 향상 |
✅ 사용
1. useReducer 불러오기
import { useReducer } from 'react';
2. reducer 함수 작성
function reducer(state, action) {
// state를 계산할 코드
return {
// 계산된 state를 반환
};
}
reducer 함수
: state가 어떻게 업데이트 되는지 지정하는 순수 함수
- 리듀서 함수는 반드시 순수 함수여야 한다.
- 현재 상태(state)와 액션(action)을 인수로 받아서 새로운 state를 반환한다.
- state: 현재 상태
- action: 상태를 변경할 명령을 포함하는 객체
- type: 어떤 종류의 액션인지 나타낸다.
- action 객체에서 가장 중요한 필드
- 이 type 필드의 값에 따라 reducer가 어떻게 동작할지 결정된다.
- payload(선택): 상태 변경에 필요한 추가 데이터
- type: 어떤 종류의 액션인지 나타낸다.
function reducer(state, action) {
switch (action.type) {
case 'added':
return [...state, { id: action.id, text: action.text }];
case 'changed':
return state.map((task) =>
task.id === action.task.id ? action.task : task
);
case 'deleted':
return state.filter((task) => task.id !== action.id);
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
3. useReducer 사용
const [state, dispatch] = useReducer(reducer, initialState, init?);
반환값
- state: 현재 state
- 첫번째 렌더링에서의 state는 init(initialArg) 또는 initialArg로 설정된다.
(init이 없을 경우 initialArg로 설정된다)
- 첫번째 렌더링에서의 state는 init(initialArg) 또는 initialArg로 설정된다.
- dispatch 함수: state를 업데이트하고 리렌더링을 일으키는 함수
- dispatch에 액션 객체를 전달하여 상태를 변경한다.
매개변수
- reducer: state가 어떻게 업데이트 되는지 지정하는 순수 함수
- 리듀서 함수는 반드시 순수 함수여야 한다.
- 현재 상태(state)와 액션(action)을 인수로 받아서 새로운 state를 반환한다.
- initialState: 초기 state 값
- 컴포넌트가 처음 렌더링될 때의 초기 상태
- init: 초기 state를 반환하는 초기화 함수
- 이 함수가 인수에 할당되지 않으면 초기 state는 initialState로 설정된다.
- 할당되었다면 초기 state는 init(initialState)를 호출한 결과가 할당된다.
export default function Counter() {
const initialState = { count: 0 };
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>Count: {state.count}</h1>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
예제 1. 카운터 컴포넌트
- reducer 함수 작성
- reducer 함수는 현재 상태와 액션을 받아서 새로운 상태를 반환한다.
- action.type이 increment인 경우 상태의 count를 1 증가시키고, decrement인 경우 1 감소시킨다.
- 초기 상태 설정
- initialState는 컴포넌트가 처음 렌더링될 때 사용되는 초기 상태이다.
- 여기서는 { count: 0 }으로 설정했다.
- useReducer 사용
- useReducer 훅을 사용하여 reducer 함수와 initialState를 전달한다.
- state는 현재 상태를 나타내고, dispatch는 액션을 보낼 때 사용된다.
- 컴포넌트 내에서 상태 관리
- dispatch를 사용하여 상태를 변경할 수 있다.
- dispatch 함수에 { type: 'increment' } 또는 { type: 'decrement' }를 전달하여 상태를 업데이트한다.
import React, { useReducer } from 'react';
// reducer 함수 작성
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: action.payload };
default:
return state;
}
}
// 초기 상태와 초기화 함수
const initialState = { count: 0 };
function Counter({ initialCount }) {
// useReducer 사용
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: initialCount })}>Reset</button>
</div>
);
}
export default Counter;