컴포넌트 트리에 데이터 공급하기

로이·2024년 6월 4일

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;

0개의 댓글