useReducer로 자주 사용하지는 않았지만 알아둘 필요가 충분히 있다고 생각함.
const [state, dispatch] = useReducer(reducer, initState)
로 사용되고 여기서 state는 사용할 state이고 dispatch는 action을 실행시키는 함수이다. 이 action을 통해 state를 어떻게 업데이트할지 정할수 있다. reducer는 컴포넌트 외부에서 정의할 수 있는 state를 업데이트하는 로직이며 const reducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return ~~
default:
return;
}
};
이런식으로 정의된다.
reducer함수의 첫번째 인자인 state는 업데이트 이전의 state이고 action은 dispatch함수가 발생시키는 action이다.
이 action을 통해서 어떤식으로 state를 업데이트할지 정할수 있다.
이는 redux
에서도 사용하는 action을 발생시키는 dispatch함수를 통해서 reducer함수에서 이전 state와 action으로 새로운(업데이트 된) state를 리턴해주는 방시과 같다.
그리고 추가로 useReducer의 세번째 인자에는 초기화하는 함수가 들어갈 수 있는데 이는 reducer함수 외부에서 초기 state를 설정하는 로직이 존재할수 있다는 것이고 단순히 reducer에서만 state를 업데이트하기보단 그 외부에 state 업데이트 로직의 추가로 더 다양한 일을 하게 도와줄수 있다.
예시를 확인해보자
코드
import React, { useReducer, useRef } from "react";
import "./styles.css";
const initState = [];
const ACTIONS = {
ADD_TODO: "ADD_TODO",
DELETE_TODO: "DELETE_TODO",
TOGGLE_TODO: "TOGGLE_TODO"
};
let id = 0;
const reducer = (state, action) => {
switch (action.type) {
case ACTIONS.ADD_TODO:
return [
...state,
{ id: action.payload.id, thing: action.payload.thing, checked: false }
];
case ACTIONS.DELETE_TODO:
return state.filter((todo) => todo.id !== action.payload.id);
case ACTIONS.TOGGLE_TODO:
return state.map((todo) => {
if (todo.id === action.payload.id) {
return !todo.checked
? { ...todo, checked: true }
: { ...todo, checked: false };
}
return todo;
});
default:
return;
}
};
export default function App() {
const inputRef = useRef();
const [state, dispatch] = useReducer(reducer, initState);
const addTodo = (e) => {
e.preventDefault();
const newTodo = inputRef.current.value;
dispatch({
type: ACTIONS.ADD_TODO,
payload: { id: ++id, thing: newTodo }
});
inputRef.current.value = "";
inputRef.current.focus();
};
const deleteTodo = (id) => {
dispatch({ type: ACTIONS.DELETE_TODO, payload: { id } });
};
const toggleTodo = (id) => {
dispatch({ type: ACTIONS.TOGGLE_TODO, payload: { id } });
};
return (
<>
<form onSubmit={addTodo}>
<input type="text" ref={inputRef} />
<button>추가</button>
</form>
<ul>
{state.map((todo) => {
return (
<li key={todo.id}>
<span
style={
todo.checked ? { textDecorationLine: "line-through" } : null
}
>
{todo.thing}
</span>
<button
onClick={() => {
deleteTodo(todo.id);
}}
>
삭제
</button>
<button
onClick={() => {
toggleTodo(todo.id);
}}
>
toggle
</button>
</li>
);
})}
</ul>
</>
);
}
요즘에 자주 사용하지 않아서 익숙치 않던던 액션을 dispatch시켜서 reducer에서 state업데이트하는 방식을 다시금 사용하게되어 좋았다.