const MyContext = createContext();
function Child1()
{
console.log("child 1 rerender");
return <div>Child 1</div>
}
function Child2()
{
console.log("child 2 rerender");
const light = useContext(MyContext);
return <div>{light ? "light" : "dark"}</div>
}
function App() {
console.log("app rerender");
const [light, setLight] = useState(true);
const [count, setCount] = useState(0);
return (
<>
<MyContext.Provider value={light}>
<Child1 />
<Child2 />
</MyContext.Provider>
<div onClick={()=>setLight( l=>!l )}>변화</div>
<div onClick={()=>setCount( l=>l+1 )}>{count}</div>
</>
)
}
이런 코드를 짰을 때, light 상태를 변화시키면 자식 컴포넌트가 모두 리렌더링되는 경험을 볼 수 있을 것이다. 그래서 사람들은 "Context Provider의 value가 바뀌면 자식이 모두 리렌더링되는구나! 렌더링에 안 좋겠네?"라고 생각하게 될 것이다.
물론, 이 코드에서 "변화" 버튼을 누르면 Child1과 Child2가 리렌더링되는 건 맞으나, Child1이 리렌더링되는 까닭은 Context Provider가 바뀌기 때문이 아니라, 그냥 App의 state가 바뀌었기 때문이다. 부모 컴포넌트의 state가 바뀌면 부모가 렌더링되고, 자식도 같이 렌더링되기 때문이다.
const MyContext = createContext();
function Child1()
{
console.log("child 1 rerender");
return <div>Child 1</div>
}
function Child2()
{
console.log("child 2 rerender");
const light = useContext(MyContext);
return <div>{light ? "light" : "dark"}</div>
}
const MemoWrapper = memo( ()=>{
return <>
<Child1 />
<Child2 />
</>
});
function App() {
console.log("app rerender");
const [light, setLight] = useState(true);
const [count, setCount] = useState(0);
return (
<>
<MyContext.Provider value={light}>
<MemoWrapper />
</MyContext.Provider>
<div onClick={()=>setLight( l=>!l )}>변화</div>
<div onClick={()=>setCount( l=>l+1 )}>{count}</div>
</>
)
}
이 경우에는 변화 버튼을 클릭했을 때, MemoWrapper에 props가 변화하지 않아서 MemoWrapper가 리렌더링되지 않음에도 불구하고, Child2는 리렌더링되고 있다. MyContext.Provider의 value가 바뀌었고, Child2가 MyContext를 소비하고 있기 때문이다.
즉, useContext는 다음의 2개의 역할을 한다.
Context API를 이용해서 상태를 관리할 때, 하위 컴포넌트를 memo로 감쌌을 때와 감싸지 않았을 때의 동작은 다음과 같다.