📍useMemo 로 객체 or 배열로 인한 불필요한 리렌더링 제거
const Pagination = ({ pagesNumbers }) => {
const augmentedValues = pagesNumbers.map(page => page.number + 2);
return <RenderPages data={augmentedValues} />;
};
보기엔 문제가 없어보이지만…
객체 또는 배열이 컴포넌트 내부에서 선언되면, 매 렌더링마다 별도의 인스턴스로 간주된다
- 동일한 데이터를 갖고 있어도 리렌더링을 유발
- 메모리 주소가 달라지기 때문
- 이는 각 컴포넌트마다 새로운 계산을 필요로하며 그에 따라 렌더링도 늦어짐..
useMemo Hooks를 통해 계산 결과를 메모이제이션할 수 있다
const Pagination = ({ pagesNumbers}) => {
const augmentedValues = useMemo(() => pagesNumbers.map(page => page.number + 2), [pagesNumbers]);
return <RenderPages data={augmentedValues} />;
};
- 데이터가 동일하다면, 이미 계산된 값이 메모이제이션되어 새로운 계산을 스킵한다
- depndency 배열이 달라지는 경우에만 재계산한다
하지만, useMemo Hooks 를 남용해서도 안된다
- 종속성이 변경될 때까지 함수의 결과를 저장하므로 메모리 비용이 발생
- 또한, 매 렌더링 이후 dependency를 비교해야 하는데, 이것도 성능 비용임을 기억해야 함
📍useCallback 으로 함수로 인한 불필요한 리렌더링 제거
const MyButton = ({ updateState }) => {
const handleClick = (e) => {
e.preventDefault();
updateState(e);
};
return <button onClick={handleClick}>Update Props</button>;
};
컴포넌트 내부에서 선언한 함수도 매 렌더링마다 새로운 함수를 만들어낸다
- 이 함수가 자식 컴포넌트에 props로 전달되면, 불필요한 리렌더링을 유발한다
useCallback을 활용하면 useMemo 처럼 함수를 메모이제이션할 수 있다
- 장점은 물론 단점도 useMemo와 동일하다
- 익명 함수도 함수처럼 리렌더링 시 새 인스턴스를 만든다
- 예시
const MyButton= () => {
return <button onClick={() => console.log("Inline function!")}>Click here</button>;
};
📍map() 내부 콜백의 2번째 인수인 index 사용 지양하기
items 배열의 인덱스를 key로 사용하면 불필요한 리렌더링을 유발할 수 있다
- items 배열에 새로운 원소가 추가되거나 제거되면 불필요한 리렌더링 발생…
const MyList = ({ items }) => {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
};
이렇게 item이 내부적으로 필드를 갖게 하여, uid를 부여하면 내용(name)이 바뀌지 않는 이상, 리렌더링되지 않는다
const MyList = ({ items }) => {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
📍React.memo 로 불변하는 값 또는 컴포넌트 메모이제이션하기
불변하는 props를 가진 컴포넌트의 경우라면, React.memo 로 불필요한 연산을 막을 수 있다
const DumbRenderOfText= ({ textThatWontChange }) => {
return <div>{textThatWontChange}</div>;
};
export default React.memo(DumbRenderOfText);
이렇게 하면 props가 변경된 경우에만 컴포넌트를 리렌더링한다
📍참고자료