오늘은 컴포넌트가 리렌더링 되는 경우 1가지를 얘기해 보려고 합니다.
내부적으로 더 자세하게 구분되어야 하겠지만 크게는
로 요약할 수 있을 것 같습니다. 이 부분에 대해서 많은 사람들이 이미 알고 있을 것이라고 생각합니다.
간단한 예시입니다.
function Parent() {
return (
<div>
<ChildA />
</div>
);
}
function ChildA() {
console.log("A is rendered!");
return <div>ChildA</div>;
}
해당 구조에서 Parent가 리렌더링 된다면 자식 컴포넌트인 ChildA도 리렌더링이 일어날 것입니다.
여기서 구조를 조금만 바꿔보겠습니다.
function App({ children }) {
return (
<Parent>
<ChildA />
</Parent>
);
}
function Parent({ children }) {
return (
<div>{children}</div>
);
}
function ChildA() {
console.log("A is rendered!");
return <div>ChildA</div>;
}
크게 어색한 점은 없습니다. 많이 사용하듯 children props를 사용하여 자식 컴포넌트를 렌더링 한 모습입니다. 이제 Parent 컴포넌트에 리렌더링을 발생시켜 보겠습니다.
function Parent({ children }) {
const [state, setState] = useState({});
useEffect(() => {
const id = setInterval(() => setState({}), 1000);
return () => clearInterval(id);
}, [state]);
console.log("parent is rendered");
return (
<div>{children}</div>
);
}
결과는 어떻게 될까요?
Parent 컴포넌트는 리렌더링이 발생하지만 자식 컴포는트는 최초 렌더링 이후 리렌더링이 발생하지 않는 모습을 확인할 수 있습니다. 이 부분이 바로 글을 작성하게 된 계기입니다. 이러한 현상의 원인을 이미 알고 있던 사람들도 많겠지만 저는 최근 개발 도중 꽤나 삽질을 하다가 그 원리를 알게 되었습니다.
다시 앞으로 돌아가 보겠습니다. 언제 리렌더링이 발생할까요?
이것들은 독자적인 이유로 보이지만 사실 유기적인 모습을 띄고 있습니다.
리액트 공식 문서를 보신 분들이라면 아시겠지만 앞선 Parent 컴포넌트에서 리턴한 ChildA 컴포넌트는 React.createElement(ChildA, null)로 컴파일되고 이후 {type: ChildA, props: {}} 형태의 ReactElement가 생성됩니다.
여기서 주목할 점은 props입니다. 컴포넌트가 생성될 때마다 기본적으로 props는 새 객체 형태로 생성됩니다. 이로써 리액트는 불변성을 감지하고 리렌더링을 발생시킬 수 있습니다. 즉 props가 변경되었기 때문에 리렌더링이 발생하게 됩니다.
하지만 자식 컴포넌트를 children props로 전달하면 어떻게 될까요? ChildA에 대한 React.createElement(ChildA, null)가 호출 될 일이 없기 때문에 리렌더링이 발생하지 않았던 것입니다.
이 같은 원리를 깨닫기까지 많은 삽질과 서칭이 있었습니다.. 결국 공식 문서의 내용을 좀 더 잘 이해했다면.. 이라는 생각이 밀려오는 경험이었습니다.