React - useCallback

Hunter Joe·2025년 4월 16일

Syntax

const cachedFn = useCallback(Fn, dep);

Parameters

Fn :

  • 캐시하고자 하는 함수 값
  • React는 초기 렌더링 시 내가 넘긴 함수를 호출하지 않고 그대로 반환
  • 이후 렌더링에서는 아래와 같이 동작
    의존성 배열(dependencies)가 변경 O : 새로운 함수
    의존성 배열(dependencies)가 변경 X : 기존 함수 재사용

dep:

  • React는 의존성 배열의 각 항목을 이전 렌더링 때의 값과 비교할 때 Object.is 비교 알고리즘을 사용

    NOTE

    console.log(+0 === -0);           // true
    console.log(Object.is(+0, -0));   // false ❗
    
    console.log(NaN === NaN);         // false
    console.log(Object.is(NaN, NaN)); // true ✅

Example

import React, { useCallback, useState } from "react";

const Child = React.memo(
  ({ onClick, childNum }: { onClick: () => void; childNum: number }) => {
    console.log(`🔁 Child 리렌더링됨 ${childNum}`);
    return <button onClick={onClick}>Click me</button>;
  }
);

export default function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    console.log("clicked");
  };

  const cachedhandleClick = useCallback(handleClick, []);

  return (
    <div>
      <h1>Count: {count}</h1>
      <Child onClick={cachedhandleClick} childNum={1} />
      <Child onClick={handleClick} childNum={2} />
      <button onClick={() => setCount((c) => c + 1)}>Increase Count</button>
    </div>
  );
}

리액트의 렌더링 조건
1. 상태가 변화하면 리렌더
2. prop가 변화하면 리렌더
3. 부모컴포넌트가 변하면 자식 컴포넌트도 리렌더!
이 조건을 항상 기억하고 쓰면 useCallback을 통한 최적화 가능

(추가)Example + Zustand로 렌더링 최적화

// counterStore.ts
import { create } from "zustand";

interface Counter {
  count: number;
  increase: () => void;
}

export const useCounterStore = create<Counter>((set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
}));
import React from "react";
import { useCounterStore } from "../store/useCounterStore";

const Child = React.memo(({ childNum }: { childNum: number }) => {
  console.log(`🔁 Child 리렌더링됨 ${childNum}`);
  const increase = useCounterStore((state) => state.increase);

  return (
    <div>
      <button onClick={increase}>Increase Count Btn (Child)</button>
    </div>
  );
});

function ExampleZustand() {
  console.log(`🔁 Parent 리렌더링됨`);
  // const [count, setCount] = useState(0);
  const { count } = useCounterStore();

  return (
    <div>
      <h1>Count: {count}</h1>
      <Child childNum={2} />
    </div>
  );
}
export default ExampleZustand;
profile
Async FE 취업 준비중.. Await .. (취업완료 대기중) ..

0개의 댓글