이전 포스팅 들에서 리랜더링이란 말을 계속 써왔는데, React의 경우 리랜더링이 언제 일어날까? 바로 State의 값이 변경될 때이다. State 값이 변경되면 그 State 값을 가지고 있는 Component는 그 상태의 변경을 반영하기 위해서 리랜더링된다. 그렇다면 그 Component의 자식 Component들은 이런 경우에 어떻게 될까?
원칙적으로 React는 자식 component들도 전부 리랜더링하게 된다. 아래 예시 코드를 보자. State는 총 3개가 있고 그 State 에서 2개씩 짝지어서 어떤 무거운 작업을 통해서 얻은 결과물을 자식 component에 전달한다.
가장 이상적인 상황을 가정하면 첫 번째 자식 component은 num1, num2가 바뀔 때만 리랜더링되고 두 번째 자식 component는 num2와 num3가 바뀔 때만 무거운 작업을 실행해야 한다.
하지만 실제 아래 코드를 실행해서 num1, num2, num3를 변경해보면 어떤 State를 변경해도 무거운 작업이 실행되는 것을 볼 수 있다. heavySum이라는 무거운 작업은 정말 필요할 때만 실행하도록 할 수는 없을까?
export default function BlogComponent() {
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
const [num3, setNum3] = useState(0);
const heavySum = (a, b) => {
console.log("😅 무거운 작업");
return a + b;
};
const sum1 = () => heavySum(num1, num2);
const sum2 = () => heavySum(num2, num3);
return (
<div>
<button onClick={() => setNum1((prev) => prev + 1)}>
num1 + 1
</button>
<button onClick={() => setNum2((prev) => prev + 1)}>
num2 + 1
</button>
<button onClick={() => setNum3((prev) => prev + 1)}>
num3 + 1
</button>
<ChildComponent label="sum1" value={sum1()} />
<ChildComponent label="sum2" value={sum2()} />
</div>
);
}
const ChildComponent = ({ label, value }) => {
console.log(`re-rendered! label: ${label} value: ${value}`);
return <p>{`label: ${label} value: ${value}`}</p>;
};
이런 경우 사용하는 것이 useMemo이다. useMemo는 어떤 함수의 실행 값을 저장해두고 지정한 값이 변경되지 않으면 새로 계산하는 대신 저장된 값을 가져와서 사용한다.
export default function BlogComponent() {
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
const [num3, setNum3] = useState(0);
const heavySum = (a, b) => {
console.log("😅 무거운 작업");
return a + b;
};
const memoSum1 = useMemo(() => heavySum(num1, num2), [num1, num2]);
const memoSum2 = useMemo(() => heavySum(num2, num3), [num2, num3]);
return (
<div>
<button onClick={() => setNum1((prev) => prev + 1)}>
num1 + 1
</button>
<button onClick={() => setNum2((prev) => prev + 1)}>
num2 + 1
</button>
<button onClick={() => setNum3((prev) => prev + 1)}>
num3 + 1
</button>
<ChildComponent label="sum1" value={memoSum1} />
<ChildComponent label="sum2" value={memoSum2} />
</div>
);
}
const ChildComponent = ({ label, value }) => {
console.log(`re-rendered! label: ${label} value: ${value()}`);
return <p>{`label: ${label} value: ${value()}`}</p>;
};
위 코드는 heavySum을 다시 실행하지 않지만 ChildComponent의 props가 바뀌지 않았음에도 불구하고 ChildComponent를 리랜더링할 때 출력되도록 한 메시지는 그래도 출력된다.
즉 무거운 작업을 하지는 않지만 Component 자체는 다시 랜더링 된다는 것이다. 다음 포스팅에서는 props가 바뀌지 않으면 Component의 리랜더링 되지 않도록 하는 기능을 공부해보도록 하겠다.