useReducer는 상태관리 훅이다.
useState가 있는데, 왜 useReducer를 또 공부해야 하는가?
공통점
- 새로운 State를 생성하고, State를 업데이트 시키는 함수를 제공
차이점
useState
- 상태관리 로직을
컴포넌트 내부
에 작성해야 함
- 그래서 업데이트 시키는 상태관리 로직들이 복잡해지고, 길어지면 컴포넌트 내부에 상당히 많은 코드들을 작성해야 하고, 가독성이 너무나 떨어지게 됨
useReducer
- 컴포넌트
외부에 State 관리 로직 분리
가능
const [count, dispatch] = useReducer(리듀서함수, 상태의 초깃값)
function reducer(현재상태, action ) {
if(action.type === "MINUS") {
return 현재상태 - action.data
}
}
- reducer함수는 첫번째 인자로 현재상태, 두번째 인자로는 action 객체가 전달
- 그러면 action.type 조건에 따라 return으로 전달된 내용을 수행
const [count, dispatch] = useReducer(reducer, 0)
- count 변수의 상태를 변화시키기 위한
useReducer
훅
- count 변수의 초깃값은 0
- dispatch는 상태변화를 발동시키기 위한 트리거, 그러니깐 상태변화를 유발하기만 하고 직접 상태변화를 처리하지 않는 상태
- 상태변화는 reducer 함수가 처리함
<h4>{count}</h4>
<button
onClick={() => {
dispatch({
type: "MINUS",
data: 1,
});
}}
>
-
</button>
작동원리
- onClick 이벤트에 콜백 함수를 실행하도록 해놓고
onClick = {() => {}}
- dispatch를 호출(트리거)해서, reducer 상태변화 함수를 실행시킴
onClick = {() => {
dispatch()
}}
- dispatch()안에는 어떤 상태변화를 원하는지 인수(객체형태)로 전달해줘야 함
onClick = {() => {
dispatch({
type:"MINUS",
data:1,
})
}}
todolist에 useReducer 적용해보기
1단계: useState 지우기
const [todos, dispath] = useReducer(reducer, mockData)
2단계: reducer 함수 만들기
function reducer() {}
function reducer(state, action) {
if (action.type === "CREATE") {
return [...state, action.data];
}
if (action.type === "UPDATE") {
return state.map((it) =>
it.id === action.data ? { ...it, isDone: !it.isDone } : it
);
}
if (action.type === "DELETE") {
return state.filter((it) => it.id !== action.data);
}
}
3단계: 기존 setTodos를 호출했던 부분에 dispatch()를 호출
const onUpdate = (targetId) => {
dispatch({
type: "UPDATE",
data: targetId,
});
};
const onUpdate = (targetId) => {
dispatch({
type: "UPDATE",
data: targetId,
});
};
const onDelete = (targetId) => {
dispatch({
type: "DELETE",
data: targetId,
});
};
전체적으로 컴포넌트 내에 적었던(주석처리 된 코드) 코드가 useReducer사용으로 간략해진 것을 확인할 수 있음