1) 컴포넌트에서 state가 변경되었을 때
2) 컴포넌트가 내려받은 props가 변경되었을 때
3) 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 모두!
최적화(Optimiztion)
리액트에서 렌더링이 자주 일어나는 것은 좋지 않으며
이를 줄이는 작업이 최적화라고 한다.
💡최적화의 대표적인 방법
React. memo
) : 컴포넌트를 캐싱부모 컴포넌트의 state 변경으로 인해 props가 변경되지 않으면 memo
를 통해 자식 컴포넌트의 리렌더링을 방지할 수 있다.
부모 컴포넌트가 App, 자식 컴포넌트가 Box1,Box2라 하자.
export default function App() {
console.log('App 컴포넌트 렌더링');
const [count,setCount] = 0;
const handleAdd = () => {
setCount(count+1);
})
return (
<>
<p>현재 카운트 {count}</p>
<button onClick = {handleAdd}>Add</button>
<div>
<Box1/>
<Box2/>
<div>
</>
);
}
➡️ count 값이 변경될 때마다 Box1,2 컴포넌트 모두
리렌더링이 발생한다.
React.memo를 이용하면 컴포넌트를 메모리에 저장할 수 있다.
function Box1() {
console.log('Box1 렌더링')
return <div>Box1</div>;
}
export default React.memo(Box1);
➡️ 최초 렌더링 외에 부모 컴포넌트에서 상태가 변해도 자식 컴포넌트인 Box1,Box2 컴포넌트는 리렌더링이 일어나지 않는다.
const Box1 = memo(() => {
console.log('Box1 렌더링')
return <div>Box1</div>;
}
export default Box1;
함수 선언 시 memo를 쓸 경우, 이와 같이 작성할 수 있다.
인자로 들어오는 함수 자체를 기억할 때 사용된다.
만약에 Box1 컴포넌트에서 값을 초기화하는 함수를 props를 통해 받는다고 하자.
export default function App() {
console.log('App 컴포넌트 렌더링');
const [count,setCount] = 0;
const handleAdd = () => {
setCount(count+1);
})
const resetCount = () => {
setCount(0);
})
return (
<>
<p>현재 카운트 {count}</p>
<button onClick = {handleAdd}>Add</button>
<div>
<Box1 resetCount={resetCount}/>
<Box2/>
<div>
</>
);
}
------------------------------------
function Box1({resetCount}) {
console.log('Box1 렌더링')
return <div>
Box1
<button onClick={resetCount}>리셋</button>
</div>;
}
export default React.memo(Box1);
➡️ 따라서 리셋 함수를 별도로 메모리에 저장하면 된다!
useCallback
도 useEffect와 같이 의존성 배열을 넣는다.
const resetCount = useCallback(() => {
setCount(0);
},[])
🧐 리셋 버튼을 누르는 시점에서 count를 출력하면 값이
어떻게 나올까?
일단 리셋하는 함수에서 출력해보자.
const resetCount = useCallback(() => {
console.log(`${count}에서 0으로 변함`);
setCount(0);
},[])
놀랍게도 '0에서 0으로 변함'이 출력된다.
useCallback을 통해 카운트를 설정한 값(setCount(0)
)이 스냅샷으로 저장되어 있기 때문에 0으로 나온다.
리셋을 누른 시점에서의 count를 출력하고 싶으면
의존성 배열에 count 변수를 추가하면 된다.
const resetCount = useCallback(() => {
console.log(`${count}에서 0으로 변함`);
setCount(0);
},[count])
카운트 값이 변경될 때마다 useCallback이 다시 저장되기 때문에 해당 시점에서의 count를 제대로 출력하지만
Box1 컴포넌트도 리렌더링이 일어난다.
함수가 리턴하는 값 자체를 기억할 때 사용된다.
의존성 배열 역시 존재한다. 주로 무거운 일을 할 때 사용된다.
export default Counter() {
const [count,setCount] = useState(0);
const handleAdd = () => {
setCount(count+1);
}
const calculate = () => {
for(let i =0; i < 1000000;i++){
console.log('☺️');
}
return 100;
}
const value = calculate();
return (
<>
<p>{count}</p>
<button onClick = {handleAdd}>+</button>
</>
);
}
➡️ 계산 작업으로 반환한 값을 useMemo를 통해 메모리에 저장한다.
const value = useMomo(() => calculate(),[]);
👉아까와 다르게 바로바로 카운트가 증가되는 것을 볼 수 있다.