앞에서 useReducer
와 useContext
사용법을 봤었는데 이번에는 두 hook을 한파일안에 같이 사용해볼 에정이다.
TodoContext.js
import React, { createContext, useContext, useReducer, useRef } from "react";
const initialTodos = [
{
id: 1,
text: "React 공부하기",
done: true,
},
{
id: 2,
text: "velog 쓰기",
done: false,
},
{
id: 3,
text: "학교 공부하기",
done: false,
},
];
function todoReducer(state, action) {
switch (action.type) {
case "CREATE":
return state.concat(action.todo);
case "TOGGLE":
return state.map((todo) =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
);
case "REMOVE":
return state.filter((todo) => todo.id !== action.id);
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
todolist iteme들을 임의로 세개정도 만들어 주고
action
type에 따라 switch문으로 reducer
를 정의해 주었다.
const TodoStateContext = createContext();
const TodoDispatchContext = createContext();
const TodoNextIdContext = createContext();
export function TodoProvider({ children }) {
const [state, dispatch] = useReducer(todoReducer, initialTodos);
const nextId = useRef(5);
return (
<TodoStateContext.Provider value={state}>
<TodoDispatchContext.Provider value={dispatch}>
<TodoNextIdContext.Provider value={nextId}>
{children}
</TodoNextIdContext.Provider>
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
);
}
이후에 createContext
로 context
를 만들어 주고 context 의 value값으로 useReducer
로 가져온 값들을 넣어주었다.
📌 불필요한 렌더링을 막기 위해 context들을 따로 만들어줬다.
export function useTodoState() {
const context = useContext(TodoStateContext);
if (!context) {
throw new Error("Cannot find TodoProvider");
}
return context;
}
export function useTodoDispatch() {
const context = useContext(TodoDispatchContext);
if (!context) {
throw new Error("Cannot find TodoProvider");
}
return context;
}
export function useTodoNextId() {
const context = useContext(TodoNextIdContext);
if (!context) {
throw new Error("Cannot find TodoProvider");
}
return context;
}
따로 만든 context를 별도의 hook
으로 따로 뺴서 컴포넌드 단에서 컨텍스트 별로 가져갈 수 있도록 해주었다.
function TodoItem({ id, done, text }) {
const dispatch = useTodoDispatch();
const onToggle = () =>
dispatch({
type: "TOGGLE",
id,
});
const onRemove = () =>
dispatch({
type: "REMOVE",
id,
});
return (
<TodoItemBlock>
<CheckCircle onClick={onToggle} done={done}>
{done && <MdDone />}
</CheckCircle>
<Text done={done}>{text}</Text>
<Remove onClick={onRemove}>
<MdDelete />
</Remove>
</TodoItemBlock>
);
}
위 처럼 hook
으로 dispatch를 가져올 수 있고 action.tyoe
으로 함수를 사용 할 수 있다.