컴포넌트가 재렌더링되면 그 안에 있는 자식 컴포넌트도 항상 함께 재렌더링 된다.
이런 경우 자식 컴포넌트가 렌더링 시간이 오래 걸리는 무거운 컴포넌트라면 부모 컴포너트의 렌더링 시간이 문제가 될 수 있다.
(예를 들어 자식 컴포넌트가 렌더링 시간이 10초나 걸리는 무거운 컴포넌트라면 부모 컴포넌트에 있는 버튼을 누를 때마다 10초 버벅이는 불상사가 발생한다.)
그럴 때는 자식을 memo로 감싸 놓으면 된다.
테스트용으로 자식 컴포넌트를 하나 만들어 보자.
(Cart.js)
import { useState } from 'react';
function Child(){
console.log('재렌더링됨');
return <div>자식 컴포넌트임</div>
}
function Cart(){
let [count, setCount] = useState(0);
return (
<div>
<Child/>
<button onClick={()=>{ setCount(count+1) }}>+</button>
</div>
)
}
export default Cart;
Cart 컴포넌트 안에 Child 컴포넌트를 만들었다.
그리고 버튼을 누를 때 Cart 컴포넌트가 재렌더링 되게 만들었는데, 이 경우 <Child>도 재렌더링 된다.
콘솔창을 보면 버튼을 누를 때마다 Child 컴포넌트도 재렌더링 되는 것을 확인할 수 있다.
평소라면 별 문제가 없겠지만 <Child>가 렌더링이 2초 정도 걸리는 느린 컴포넌트라면?
버튼을 누를 때마다 버벅일 것이다.
그럴 때 memo라는 함수를 쓰면 "꼭 필요할 때만 <Child> 컴포넌트 재렌더링해 주세요" 라코 코드를 짤 수 있다.
memo()로 컴포넌트 불필요한 재렌더링 막기
(Cart.js)
import { memo, useState } from 'react';
let Child = memo( function(){
console.log('재렌더링됨');
return <div>자식 컴포넌트임</div>
})
function Cart(){
let [count, setCount] = useState(0);
return (
<div>
<Child/>
<button onClick={()=>{ setCount(count+1) }}>+</button>
</div>
)
}
export default Cart;
- 상단에 memo를 import 한다.
- 원하는 컴포넌트의 정의 부분을 memo()로 감싸면 된다. 컴포넌트를 let 컴포넌트명 = function(){} 이런 식으로 만들어야 감쌀 수 있다.
memo는 props가 별할 때만 재렌더링해 준다.
memo로 감싼 컴포넌트는 헛된 재렌더링을 안 시키려고 기존 props와 바뀐 props를 비교하는 연산이 추가로 진행된다. (기존 props == 신규 props) 그 다음 재렌더링 여부가 결정되는데, props가 크고 복잡하면 기존 props와 바뀐 props를 비교하는 데에도 오랜 시간이 걸릴 것이다.
props가 길고 복잡하면 memo를 사용하는 것이 오히려 안 좋을 수 있으니 꼭 필요한 곳에만 사용하는 것이 좋다.
useMemo
useMemo라는 비슷한 문법도 있는데, useEffect와 비슷한 용도이다.
컴포넌트 로드시 1회만 실행하고 싶은 코드가 있으면 거기 담으면 된다.
import {useMemo} from 'react'
function 함수(){
return 반복문 10억번 돌린 결과
}
function Cart(){
let result = useMemo(()=>{ return 함수() }, [])
return (
<>
</>
)
}
예를 들어서 반복문을 10억번 돌려야 하는 경우, 그 함수를 useMemo 안에 넣어 두면 컴포넌트 로드시 1회만 실행된다.
그러면 재렌더링마다 동작하지 않으니까 좀 호율적으로 동작할 수 있다. useEffect처럼 dependency도 넣을 수 있어서 특정 state, props가 변할 때만 실행할 수도 있다.
useEffect는 html이 모두 렌더링 된 후에 동작하지만 useMemo는 렌더링될 때 같이 동작한다.
* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.