[React] useReducer
React에서 '상태관리'에 사용되는 hook에는 useState와 useReducer가 있다.
useState는 간단한 상태관리를 위해 사용되지만,
복잡한 로직이 있는 경우에는 useReducer를 사용하는 것이 더 구조적이고 관리하기 쉽다.
useReducer의 개념과 적용한 예시 코드를 정리해보자 !
💡useReducer 란?
useReducer 는 상태(state)의 변경 로직을 컴포넌트 밖으로 분리해줌으로써 보다 명확하게 관리하는 Hook이다.
→ 컴포넌트 외부에 상태 관리 코드를 분리
ex.
function reducer(){
//...
}
function App(){
const [todos, dispatch] = useReducer(reducer);
//...
}
기본적인 사용 형태
const [state, dispatch] = useReducer(reducer, initialState);
*
state: 현재 상태
dispatch: 액션을 발생시키는 함수 (상태변화 요청/ 상태변화가 있어야하는 사실을 알림)
reducer: 상태를 실제로 변화시키는 함수 (변환기)
initialState: 초기값
*
위 useReducer은 두 가지를 리턴함
1. 현재 상태값(state)
2. 상태를 바꾸기 위한 dispatch 함수
dispatch함수를 호출함으로서,
1. action 객체가 reducer함수로 전달됨 .
2. reducer(state, action) 함수가 실행됨.
3. 새로운 상태값이 반환되고, 컴포넌트가 리렌더링됨.
ex.
버튼 클릭시 +1된 값을 화면에 렌더링하는 코드
function reducer(state, action) {
switch (action.type) {
case "INCREASE":
return state + 1;
default:
return state;
}
}
const [count, dispatch] = useReducer(reducer, 0);
const onClick = () => {
dispatch({ type: "INCREASE" });
};
onClick() 는 dispatch({ type: "INCREASE" })를 호출하고,
- 이 액션이 reducer 함수로 전달됨
- reducer는 현재 state 값과 action을 기반으로 새로운 상태값을 계산
- 새로운 상태값이 useReducer에 의해 적용됨
- count 값이 갱신되고 컴포넌트가 리렌더링됨
dispatch() → useReducer() → reducer()
To do List 앱에 적용해보기
function reducer(state, action) {
switch(action.type){
case 'CREATE':
return [action.data, ...state];
case 'UPDATE':
return state.map((item)=>
item.id === action.targetId
? {...item, isDone: !item.isDone}
: item
);
case 'DELETE':
return state.filter((item)=>item.id !== action.targetId);
default:
return state;
}
}
reducer 함수는 CREATE, UPDATE, DELETE라는 세 가지 액션을 처리한다.
- CREATE: 새로운 할 일을 리스트에 추가
- UPDATE: 특정 항목의 완료 여부를 토글
- DELETE: 특정 할 일을 삭제
App 컴포넌트에서는 이 reducer를 다음과 같이 사용한다.
const [todos, dispatch] = useReducer(reducer, mockData);
const onCreate = (content) => {
dispatch({
type: "CREATE",
data: {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime(),
},
});
};
const onUpdate = (targetId) => {
dispatch({ type: "UPDATE", targetId });
};
const onDelete = (targetId) => {
dispatch({ type: "DELETE", targetId });
};
이처럼 useReducer를 활용하면 복잡한 상태 업데이트 로직을 명확하게 분리할 수 있어 유지보수나 디버깅이 쉬워진다.
💡 useReducer를 사용하며 느낀 점
- 상태 업데이트 로직을 reducer로 분리하면서 코드 구조가 더 명확해졌다.
- dispatch를 통해 상태 변경을 요청하는 방식은 마치 Redux처럼 행동하므로, 상태 관리 흐름을 체계적으로 만들 수 있다.
- 특히 CREATE, UPDATE, DELETE 같은 명확한 액션 타입을 사용하니, 디버깅 시 콘솔에 출력되는 action 정보만 봐도 어떤 일이 일어났는지 쉽게 알 수 있었다.
💡 정리
- useReducer는 복잡한 상태를 효율적으로 관리할 수 있게 해주는 Hook이다.
- 상태 변화 로직을 한 곳에 모아두기 때문에 가독성이 좋고, 재사용성이 높다.
- dispatch와 action을 통해 상태 관리 흐름을 보다 명확히 제어할 수 있다.
단순한 상태 변경은 useState, 로직이 복잡하거나 액션이 다양한 경우는 useReducer가 적합하다는 것을 체감할 수 있었다.
상태가 복잡한 컴포넌트를 구현하려고 하는데, 로직을 간단하고 명확하게 하고자 한다?
-> useReducer를 적극적으로 활용하자 ☺️
*Ref
https://ko.react.dev/reference/react/useReducer
useReducer – React
The library for web and native user interfaces
ko.react.dev
설명이 부족한 부분이나 피드백은 댓글로 남겨주세요!
감사합니다 ☺️