📌 useReducer()
: 복잡한 상태(state) 로직을 처리하는 상태 관리 훅
- useState와 유사하지만, 상태(state) 업데이트 로직을 더 명확하게 분리하고 다양한 상태(state) 변화를 처리한다.
- 현재 상태와 액션을 받아 새로운 상태를 반환하는 리듀서 함수를 사용한다.
➡︎ 상태 변화는 액션에 의해 일어나며, 이 액션은 리듀서 함수에 전달된다.
- useReducer에서 사용하는 리듀서 함수는 순수해야 한다.
➡︎ 사이드 이펙트 코드가 들어가면 안된다. - 사용
- 상태(state) 변화가 복잡하고, 여러 가지 액션에 따라 다양한 상태 변경이 필요할 때
- 상태(state)와 관련된 로직을 분리하여 관리하고 싶을 때
- 리덕스(Redux)와 같은 상태 관리 패턴을 작은 컴포넌트 내에서 구현하고 싶을 때
⏩ useState와의 차이점
- useState
- 단순한 상태 관리를 위해 사용
- 하나의 상태 값과 상태를 변경할 수 있는 함수를 제공
- useReducer
- 복잡한 상태 로직, 여러 상태를 조작해야 하거나, 상태 변경 로직이 다양한 경우 사용
- ex) 상태 업데이트가 단일 변수 이상일 때, 로직을 별도의 리듀서 함수로 분리함으로써 코드의 가독성과 유지보수성을 높일 수 있다.
✅ 사용
1. useReducer 불러오기
import { useReducer } from 'react';
2. reducer 함수 작성
function reducer(state, action) {
// state를 계산할 코드
return {
// 계산된 state를 반환
};
}
- reducer 함수
: state가 어떻게 업데이트 되는지 지정하는 순수 함수
- 리듀서 함수는 반드시 순수 함수여야 한다.
- 현재 상태(state)와 액션(action)을 인수로 받아서 새로운 state를 반환한다.
- action: reducer 함수에 전달되는 객체 ➡︎ 상태를 어떻게 변경할지를 나타내는 정보를 담고 있다.
- type
: 어떤 종류의 액션인지 나타낸다.
- action 객체에서 가장 중요한 필드
- 이 type 필드의 값에 따라 reducer가 어떻게 동작할지 결정된다.
- payload (선택 사항)
: 추가적인 데이터가 필요할 때, payload라는 필드를 사용해 전달
- type
- action: reducer 함수에 전달되는 객체 ➡︎ 상태를 어떻게 변경할지를 나타내는 정보를 담고 있다.
export default function TaskApp() {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<h1>Prague itinerary</h1>
<AddTask
onAddTask={handleAddTask}
/>
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
3. useReducer 사용
const [state, dispatch] = useReducer(reducer, initialState, init?)
- 매개변수
- reducer
: state가 어떻게 업데이트 되는지 지정하는 순수 함수
- 리듀서 함수는 반드시 순수 함수여야 한다.
- 현재 상태(state)와 액션(action)을 인수로 받아서 새로운 state를 반환한다.
- initialState
: 초기 state 값
- 컴포넌트가 처음 렌더링될 때의 초기 상태
- init
: 초기 state를 반환하는 초기화 함수
- 이 함수가 인수에 할당되지 않으면 초기 state는 initialState로 설정된다.
- 할당되었다면 초기 state는 init(initialState)를 호출한 결과가 할당된다.
- reducer
- 반환값
useReducer는 2개의 엘리먼트로 구성된 배열을 반환한다.
- 현재 state
: 첫번째 렌더링에서의 state는 init(initialArg) 또는 initialArg로 설정된다.
(init이 없을 경우 initialArg로 설정된다) - dispatch 함수
: state를 새로운 값으로 업데이트하고 리렌더링을 일으킨다.
- 현재 state
예제 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;