예전부터 리액트로 for loop을 사용해 수많은 리스트를 그려야 하는 경우, 주로 React.memo를 사용해서 렌더 부분의 rerender를 막는 용도로 사용해왔다. 특히 RN에서의 차트를 그리는 경우(RN단에서 그려야 하는 경우,) 특히 효과적이었다.
하지만 컴포넌트를 렌더해서 내려주는 {children} 방식으로도 리렌더 현상을 막을 수 있다고 하는데, 이참에 한번 알아보면 좋다고 생각했다.
앞으로 진행하게 될 사이드 프로젝트에서 무엇보다 최대한 최적화된 코드만을 사용하면서 진행하고 싶기 때문에, 또 기록하는 것을 잊고 있었기 때문에 겸사겸사 진행해보기로 했다.
리액트 최신 docs에서 내용을 찾아보면 기존 사용하던 React.children은 deprecated되었고 children과 관련한 내용은 Passing JSX as children 항목에서 확인할 수 있었다.
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}

위의 코드에서 children의 핵심은, children을 소비하는 Card 컴포넌트가 children이 jsx라는 것밖에 모른다는 것이다.(arbitrary JSX) 즉 wrapper에서는 이미 완성된 jsx Avatar 컴포넌트를 받을 뿐이기 때문에 wrapper가 rerender 조건을 충족해도 prop으로 받는 jsx가 같다면 children은 rerender 하지 않는다.
따라서 지금까지 확인한 rerender 규칙은 다음과 같다.
a. 상위 컴포넌트가 rerender될때
b. 내부 state가 rerender 될때
c. context 내부의 밸류가 변경될 때
d. hook 이 리턴하는 밸류가 변경될 때
a. 상위 컴포넌트가 rerender 될때, 커스텀 평선이나 없다면 사용하는 prop을 shallow compare 한 후 다르다면 rerender.
b. 내부 state가 rerender 될때
c. context 내부의 밸류가 변경될 때
d. hook 이 리턴하는 밸류가 변경될 때
a. 상위 컴포넌트가 rerender 될때, jsx의 형태가 변화한다면 rerender
b. 내부 state가 rerender 될때
c. context 내부의 밸류가 변경될 때
d. hook 이 리턴하는 밸류가 변경될 때
간단한 Todo list를 만들었다.

export default function Todo({ list, addList, popList, type }: Props) {
const Item = type === 'without' ? TodoItemWithoutChildren : TodoItemWithMemo;
const isWithChildren = type === 'withChildren';
const [, setUseless] = useUseless();
return (
<section>
<strong>{type}</strong>
<TodoForm addList={addList} /> // 리스트 추가
<TodoHandle label="pop" popList={popList} /> // 리스트 제거
<TodoHandle
label="setUseless"
popList={() => setUseless((prev) => !prev)}
/> // 의미없는 hook 값의 변경
{!isWithChildren &&
list.map((item, index) => (
<Item index={index} item={item} key={item.id} />
))}
{isWithChildren && (
<WithChildren>
{list.map((item, index) => (
<TodoItemWithChildren index={index} item={item} key={item.id} />
))}
</WithChildren>
)}
</section>
);
}
위의 코드에서는 총 세가지 타입의 TodoItem 리스트를 렌더하게 되는데
1. 그냥 .map만 사용하는 경우
2. memo로 item을 memoize한 경우
3. JSX를 prop으로 사용하기 위해 wrapper로 한번 더 감싼 경우이다.
1.의 경우에는 어떤 상황에서도 rerender된다.



까지를 다시 한번 알아보았다.
즉 상위 컴포넌트가 변하는 퍼포먼스 저하가 일어나는 상황에서는 memo나 JSX를 wrapper에 보내는 방법으로 코딩하는 것이 좋겠다. 하지만 어떤 방법이 좋은지는 그때그때 다를 것이고 사이드를 진행하면서 더 알아볼 기회가 있을 것이다.
참고URL:
https://kentcdodds.com/blog/optimize-react-re-renders
https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children
https://adevnadia.medium.com/react-re-renders-guide-preventing-unnecessary-re-renders-8a3d2acbdba3