useCallback

김병엽·2024년 9월 9일

성능 개선을 위한 "memoization", 그 중 "useCallback"에 대해 알아보자.


useCallback

  • 렌더링하는 사이에 함수를 캐싱할 수 있게 해주는 React Hook.
const cachedFn = useCallback(fn, dependencies)

Parameters

  • fn(함수): 캐시하려는 함수로, 어떤 인자든 받을 수 있으며, 초기 렌더링에서 반환된다. 이후 렌더링 시, 의존성이 변경되지 않았다면 React는 동일한 함수를 반환하고, 변경되었다면 새로운 함수를 반환하여 캐싱한다. React는 이 함수를 직접 호출하지 않으며, 언제 호출할지는 사용자가 결정한다.
  • dependencies(의존성 배열): 함수에서 참조하는 모든 반응형 값(예: props, state)을 포함해야 한다. 배열은 항상 같은 크기로 작성되어야 하고, 의존성 값들이 이전 값과 다를 경우 새로운 함수가 생성된다.

Returns

  • 초기 렌더링 시, useCallback은 전달된 함수를 반환한다.
  • 이후 렌더링에서는 의존성 배열이 변경되지 않으면 이전 렌더링에서 저장된 함수를 반환하고, 변경되었다면 새로 전달된 함수를 반환한다.

Caveats

  • useCallback은 컴포넌트나 커스텀 훅의 최상위에서만 호출할 수 있으며, 반복문이나 조건문 안에서는 호출할 수 없다. 또한, React는 특별한 이유가 있을 때만 캐시된 함수를 폐기한다. 예를 들어, 개발 환경에서 파일을 수정하거나, 컴포넌트가 초기 마운트 중에 중단되면 캐시를 폐기할 수 있다.
  • useCallback은 주로 성능 최적화를 위해 사용되며, 그렇지 않다면 stateref가 더 적절할 수 있다.

Usage

Updating state from a memoized callback

function TodoList() {
  const [todos, setTodos] = useState([]);

  const handleAddTodo = useCallback((text) => {
    const newTodo = { id: nextId++, text };
    setTodos([...todos, newTodo]);
  }, [todos]);
  // ...

todos 배열을 의존성으로 사용하여 새 할 일을 추가하는 함수 handleAddTodo를 정의한다. 하지만 이렇게 하면 todos 배열이 변경될 때마다 함수가 재생성된다.

function TodoList() {
const [todos, setTodos] = useState([]);

const handleAddTodo = useCallback((text) => {
  const newTodo = { id: nextId++, text };
  setTodos(todos => [...todos, newTodo]);
}, []); 
// ...

이 예시에서는 상태를 업데이트할 때 이전 상태를 읽기 위해 업데이터 함수를 사용한다.
이 방법은 todos를 의존성 배열에서 제거할 수 있어 의존성을 줄인다. 상태 업데이트는 React에게 "이전 상태를 받아서 새 상태를 계산하라"는 방식으로 전달된다.

useCallback 훅에 빈 의존성 배열 []을 사용했기 때문에 함수는 재생성되지 않는다.
컴포넌트가 처음 렌더링될 때 한 번 생성되고, 이후 렌더링 시에도 재생성되지 않고 동일한 함수가 계속해서 사용된다.


Reference

React/useCallback

profile
선한 영향력을 줄 수 있는 개발자가 되자, 되고싶다.

0개의 댓글