상세 컨텐츠

본문 제목

[React]Your Guide to React.useCallback() 요약

React

by 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()이 필요함

  1. React.memo()로 감싸져있는 자식 컴포넌트에 함수 객체를 넘김
  2. 그 함수객체가 다른 useEffect() 같은 훅함수 디펜던시에 걸려 있음
  3. 게다가 함수객체 안에 내부 상태를 들고 있음
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. 요약

  • "최적화를 하기 전에 프로파일링을 해라"
  • 성능에 대해 수치화를 해라
  • 그리고 성능 향상과 코드 복잡도 중에서 선택하라