Context
리액트에서 Context는 같은 문맥 아래에 있는 컴포넌트 그룹에 데이터를 공급하는 기능이라는 의미로, Context를 이용하면 단계마다 일일이 Props를 전달하지 않고도 컴포넌트 트리 전역에 데이터를 공급할 수 있어 Props Drilling 문제를 간단히 해결할 수 있음
Context에 데이터 공급하기
import React from 'react';
const MyContext = React.createContext(defaultValue);
function App() {
const data = 'data';
return (
<div>
<Header />
<MyContext.Provider value={data}>
<Body />
</MyContext.Provider>
</div>
);
}
export default App;
Context가 공급하는 데이터 사용하기
import { useContext } from 'react';
function Main() {
const data = useContext(MyContext);
(...)
}
Context 정리
createContext를 이용해 Context를 만들고, 값을 공급할 컴포넌트를 Context.Provider로 감싸서 공급하고, 함수 useContext를 호출해 Context가 공급하는 값을 불러와 사용함
Context로 [할 일 관리] 앱 리팩토링하기
TodoContext를 만들어 데이터 공급하기
// App.jsx
(...)
export const TodoContext = React.createContext();
function App() {
(...)
return (
<div className="App">
<Header />
<TodoContext.Provider value={{ todo, onCreate, onUpdate, onDelete }}>
<TodoEditor />
<TodoList />
</TodoContext.Provider>
</div>
);
}
export default App;
TodoList 컴포넌트에서 Context 데이터 사용하기
// TodoList.jsx
import { TodoContext } from "../App";
(...)
const TodoList = () => {
const { todo } = useContext(TodoContext);
(...)
};
export default TodoList;
TodoItem 컴포넌트에서 Context 데이터 사용하기
// TodoItem.jsx
import { TodoContext } from "../App";
const TodoItem = ({ id, content, isDone, createdDate }) => {
const { onUpdate, onDelete } = useContext(TodoContext);
(...)
};
export default TodoItem;
TodoEditor 컴포넌트에 데이터 공급하기
// TodoEditor.jsx
import { TodoContext } from "../App";
const TodoEditor = () => {
const { onCreate } = useContext(TodoContext);
(...)
};
export default TodoItem;
리팩토링이 잘 되었는지 확인하기
할 일 아이템을 생성한 후 콘솔을 확인하면, 모든 TodoItem 컴포넌트가 리렌더되는 것을 알 수 있으며, 이는 React.memo가 리팩토링 이후 정상적으로 동작하지 않음을 의미
문제의 원인 파악하기
todo가 업데이트되면, 이와 동시에 TodoContext.Provider에 전달되는 Props도 업데이트되므로, onCreate, onUpdate, onDelete만 받는 컴포넌트도 불필요한 리렌더가 발생하게 됨
구조 재설계하기
[ Context를 역할에 따라 분리 ]
- TodoStateContext: todo가 업데이트되면 영향받는 컴포넌트를 위한 Context
- TodoDispatchContext: dispatch 함수 onCreate, onUpdate, onDelete 가 변경되면 영향을 받는 컴포넌트를 위한 Context
재설계된 구조로 변경하기
// App.jsx
(...)
export const TodoStateContext = React.createContext();
export const TodoDispatchContext = React.createContext();
function App() {
(...)
const memoizedDispatches = useMemo(() => {
return { onCreate, onUpdate, onDelete };
}, []);
return (
<div className="App">
<Header />
<TodoStateContext.Provider value={todo}>
<TodoDispatchContext.Provider value={memoizedDispatches}>
<TodoEditor />
<TodoList />
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
</div>
);
}
export default App;