다양한 컴포넌트로 이루어진 제어컴포넌트에서 prop drilling이 발생하고 있었고 이를 해결하기 위해서 제어 컴포넌트 내에서 context API 를 활용해서 이 문제를 해결하려고 했다.
예를 들어, AddCard 컴포넌트 하위의 CardForm 컴포넌트가 있고 그 하위에 CardNumberInput 컴포넌트가 있고 또 하위에 Input 컴포넌트가 있는 상황을 생각해보자.
input 값으로 사용자의 입력이 들어오면 제어 컴포넌트 중 가장 상위에 있는 AddCard 가 갖고 있는 card state를 업데이트 해줘야 한다.
Context API 를 사용하지 않았을 때는 updateCard 라는 state를 변경해주는 함수를 모든 컴포넌트가 props로 가져야 했다. 실제로 CardForm, CardNumberInput 컴포넌트에서는 prop을 넘겨주기만 할 뿐 사용하지 않기 때문에 불필요한 prop을 갖고 있게 되는 것이다.
위와 같은 상황에서 제어 컴포넌트 안에서만 공유되는 객체를 관리하기 위해서 Context API를 사용했다.
그런데 현재 Context 가 앱의 최상위 컴포넌트에서 전역으로 사용되는 것이 아니라 앱의 일부인 제어 컴포넌트의 범주 안에서만 공유되는 객체로 사용되고 있다.
AddCard.jsx
function AddCard() {
const [card, setCard] = useState(getCard());
const updateCard = (name, value) => {
setCard((prevCard) => {
return { ...prevCard, [name]: value };
});
};
...
return (
<AddCardContext.Provider value={card, updateCard}>
<h2 className="page-title">카드 추가</h2>
<Card />
<AddCardForm />
</AddCardContext.Provider>
);
}
...
그래서 Context를 선언하고 있는 AddCard 컴포넌트에서 context Provider를 통해서 value를 제공하고 있다. 해당 컴포넌트가 상위의 state 변화에 따라 리렌더링 될 때 위와 같이 value에 객체나 배열 등의 형태로 넘겨주게 되면 컴포넌트가 리렌더링 될 때 매번 새로운 객체를 생성해서 비교 알고리즘에 의해 다른 값이라고 생각해서 context를 구독하는 하위의 컴포넌트가 값이 변경되지 않았음에도 불구하고 다시 렌더링 되는 문제가 생긴다.
그렇기 때문에 useMemo 훅을 사용하라는 warning을 주게 되고 이 문제를 해결하기 위해서 useMemo 훅을 사용했다.
위 코드에서 useMemo 훅만 추가를 해주면 의미없이 리렌더링되는 부분을 최적화할 수 있다.
AddCard
컴포넌트 안에 아래 코드를 추가하고 value 값에 넣어줌으로써 문제를 해결했다.
const contextValue = useMemo(() => ({ card, updateCard }), [card]);
...
return (
<AddCardContext.Provider value={contextValue}>
<Card />
<AddCardForm />
</AddCardContext.Provider>
);
이렇게 하면 값이 변경될때만 하위 컴포넌트들을 리렌더링 하기 때문에 최적화 달성이 가능해진다.
useMemo inside context
StackOverflow - is useMemo required to manage state via the context API in reactjs?