출처 : 벨로퍼트와 함께하는 모던 리액트
https://react.vlpt.us/mashup-todolist/02-manage-state.html
벨로퍼트의 모던 리액트 강좌를 따라 열심히 투두리스트를 만드는 와중,
Context API 개념이 등장했다.
먼저 Context API를 도입하기전, 개인적으로 투두리스트의 기능을 나름대로 구현하였는데 이때 각 컴포넌트에서 발생하는 이벤트들을 최상단 컴포넌트로 가져오기 위해서 props를 중간중간 계속 전달해주었다.
그림으로 보면 이런 그림이 된다.
// APP 함수에서 checkDoneItem의 기능을 만들고 이를 TodoList로 전달한다.
const App = () => {
const [todoItemArray, setTodoItemArray] = useState([]);
const checkDoneItem = (id) => {
const checkTodoItemArray = todoItemArray.map(
item => {
if (id === item.id) {
item.done = !item.done;
}
return item;
});
setTodoItemArray(checkTodoItemArray);
}
return (
<>
<GlobalStyle />
<TodoTemplate>
<TodoList checkDoneItem={checkDoneItem} />
</TodoTemplate>
</>
);
}
// TodoList에서 checkDoneItem 함수를 다시 TodoItem에 넘겨준다.
const TodoList = ({checkDoneItem}) => {
return (
<TodoItem
checkDoneItem={checkDoneItem}
>
</TodoItem>
)
});
}
// TodoItem에서 checkDoneItem을 호출한다.
const TodoItem = ({checkDoneItem }) => {
const handleCheckDone = () => {
checkDoneItem();
};
return (
<TodoItemBlock>
<CheckCircle onClick={handleCheckDone} </CheckCircle>
</TodoItemBlock>
)
이러한 단점을 보완하기에 나온 것이 바로 Context API이다.
useReducer와 useContext를 활용하면, provider로 선언된 내부에서는 해당 context를 접근할 수 있다.
import React, { createContext, useReducer, useRef } from "react";
const intitalTodos = [
{
id: 1,
text: "프로젝트 생성하기",
done: true,
},
{
id: 2,
text: "컴포넌트 스타일링하기",
done: true,
},
{
id: 3,
text: "Context 만들기",
done: false,
}
];
const 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}`);
}
};
export const TodoStateContext = createContext();
export const TodoDispatchContext = createContext();
export const TodoNextIdContext = createContext();
export const TodoProvider = ({ children }) => {
const [state, dispatch] = useReducer(todoReducer, intitalTodos);
const nextId = useRef(5);
return (
<TodoStateContext.Provider value={state}>
<TodoDispatchContext.Provider value={dispatch}>
<TodoNextIdContext.Provider value={nextId}>
{children}
</TodoNextIdContext.Provider>
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
);
};
// 상단 app에서 context를 사용하면 하위 컴포넌트에서 다 활용 가능
const App = () => {
const [todoItemArray, setTodoItemArray] = useState([]);
const checkDoneItem = (id) => {
const checkTodoItemArray = todoItemArray.map(
item => {
if (id === item.id) {
item.done = !item.done;
}
return item;
});
setTodoItemArray(checkTodoItemArray);
}
// TodoProvider 선언
return (
<>
<TodoProvider>
<GlobalStyle />
<TodoTemplate>
<TodoList checkDoneItem={checkDoneItem} />
</TodoTemplate>
</TodoProvider>
</>
);
}
코드만 봤을 때는 복잡해 보일 수도 있지만, 프로젝트가 커지고 관리해야할 컴포넌트들이 늘어난다면, Context API는 굉장한 효용성을 제공한다.