React
[React]Your Guide to React.useCallback() 요약
stayhungri
2022. 2. 9. 00:12
Your Guide to React.useCallback() 요약
https://dmitripavlutin.com/dont-overuse-react-usecallback/
- 글쓴이 페북에 누군가가 그의 팀메이트로부터 이런 소리를 들었다고 글을 남김. 그게 이 글을 쓰게된 발단이 됨
- "모든 콜백 함수는 자식의 불필요한 리렌더링을 막기 위해 메모이제이션을 해야 함"
- 자식에게 넘기는 인라인 함수는 모두 useCallback을 써야한다는 뜻
import React, { useCallback } from 'react';
function MyComponent() {
const handleClick = useCallback(() => {
// handle the click event
}, []);
return <MyChild onClick={handleClick} />;
}
그래서 글쓴이가 useCallback()을 올바르게 쓰는 방법에 대해 정리
1. 함수들이 같은지 검사하는 원리 이해
- 리액트에서 함수가 동일한지 검사할 때 '===' 을 사용
- 새로 생성된 함수의 코드가 이전 함수와 코드는 같더라도 동일성 검사에서는 false가 나옴
function factory() {
return (a, b) => a + b;
}
const sum1 = factory();
const sum2 = factory();
sum1(1, 2); // => 3
sum2(1, 2); // => 3
sum1 === sum2; // => false
sum1 === sum1; // => true
2. useCallback()의 목적
- 인라인 함수는 비용이 저렴해서 컴포넌트별로 몇개의 인라인함수 재생성은 문제가 안됨
function MyComponent() {
// handleClick is re-created on each render
const handleClick = () => {
console.log('Clicked!');
};
// ...
}
이런 경우에 useCallback()이 필요함
- React.memo()로 감싸져있는 자식 컴포넌트에 함수 객체를 넘김
- 그 함수객체가 다른 useEffect() 같은 훅함수 디펜던시에 걸려 있음
- 게다가 함수객체 안에 내부 상태를 들고 있음
import { useCallback } from 'react';
function MyComponent() {
// handleClick is the same function object
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);
// ...
}
3. 굿 케이스
- React.memo 로 랩핑되어 있는 자식에게 콜백함수를 넘겨는데 그 함수가 prop 을 들고 있거나 state 를 들고 있는 경우
import useSearch from './fetch-items';
function MyBigList({ term, onItemClick }) {
const items = useSearch(term);
const map = item => <div onClick={onItemClick}>{item}</div>;
return <div>{items.map(map)}</div>;
}
export default React.memo(MyBigList);
import { useCallback } from 'react';
export function MyParent({ term }) {
const onItemClick = useCallback(event => {
console.log('You clicked ', event.currentTarget);
}, [term]);
return (
<MyBigList
term={term}
onItemClick={onItemClick}
/>
);
}
4. 나쁜 케이스
- 굿 케이스가 아닌 경우. 아마 대부분?
- inline function 은 저렴하니 재생성에 대해 너무 신경쓰지 마라
import { useCallback } from 'react';
function MyComponent() {
// Contrived use of `useCallback()`
const handleClick = useCallback(() => {
// handle the click event
}, []);
return <MyChild onClick={handleClick} />;
}
function MyChild ({ onClick }) {
return <button onClick={onClick}>I am a child</button>;
}
import { useCallback } from 'react';
function MyComponent() {
const handleClick = () => {
// handle the click event
};
return <MyChild onClick={handleClick} />;
}
function MyChild ({ onClick }) {
return <button onClick={onClick}>I am a child</button>;
}
5. 요약
- "최적화를 하기 전에 프로파일링을 해라"
- 성능에 대해 수치화를 해라
- 그리고 성능 향상과 코드 복잡도 중에서 선택하라