React Context API 렌더링 성능 개선방안

React에서 Context API와 상태관리에서 언급했듯이 Context API는 context의 참조하지 않는 값이 변경되더라도 전체가 렌더링되는 성능 관련 이슈가 있다.

이에 대한 해결방안을 알아보았다.

1. Context 분할

Context 분할을 통해 해결할 수 있다.

1
2
3
4
5
6
7
const state1 = {
familyname: "park",
};

const state2 = {
firstname: "sunghyeon",
};

하지만 상태가 많아져 유지보수에 큰 어려움을 겪게될 수 있다.

2. React.memo

React.memo를 사용하는 것으로도 해결할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const FirstName = () => {
const [state, dispatch] = React.useContext(NameContext);
console.log("render FirstName");
return (
<div>
First Name:
<input
value={state.firstName}
onChange={(event) => {
dispatch({ type: "setFirstName", firstName: event.target.value });
}}
/>
</div>
);
};
export default React.memo(FirstName);

위와같이 단순히 export 시점에 React.memo를 추가하여도 해당 컴포넌트에더 useContext를 통해 NameContext가 반환되고 있어 NameContext가 바뀔 시 렌더링이 진행된다.

때문에 props drilling을 섞어서 해결해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const InnerFirstName = React.memo(({ firstName, dispatch }) => (
<div>
First Name:{" "}
<input
value={firstName}
onChange={(event) => {
dispatch({ type: "setFirstName", firstName: event.target.value });
}}
/>
</div>
));
const FirstName = () => {
const [state, dispatch] = React.useContext(NameContext);
console.log("render FirstName");
return <InnerFirstName firstName={state.firstName} dispatch={dispatch} />;
};
export default FirstName;

이렇게 작성하게 되면 FirstName 컴포넌트는 여전히 리렌더링 되지만 NameContext의 firstName값이 변경되지 않는다면 InnerFirstName 컴포넌트는 리렌더링되지 않는다.

단순히 Wrapper Component를 만드는 것이므로 성능상의 문제가 되지 않는다.

3. useMemo

React.memo가 다른 부가적인 값에 의해 의도한 대로 동작하지 않는 경우 useMemo를 사용하여 해결할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const FirstName = () => {
const [state, dispatch] = React.useContext(NameContext);
console.log("render FirstName");
return useMemo(
() => (
<div>
First Name:{" "}
<input
value={state.firstName}
onChange={(event) => {
dispatch({ type: "setFirstName", firstName: event.target.value });
}}
/>
</div>
),
[firstName, dispatch]
);
};
export default FirstName;

방법 2와 동일하게 FirstName 컴포넌트의 렌더를 막지는 못하지만, return에서 useMemo를 사용함으로써 렌더링을 강제로 막는 모습이다.

하지만 위 방법은 관리하는 state,props가 늘어날 때마다 useMemo의 dependency에도 추가를 해줘야해서 가장 유지보수가 힘든 방법이다.

Ref

ContextAPI 렌더링 이슈

댓글