You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用useCallback对高阶函数进行Memoize以避免子组件重渲染?

解决useCallback中高阶函数返回值的Memoize问题

嘿,这个场景确实很容易踩坑——你用useCallback包裹了那个函数生成器memoizedFnCreator,它本身的引用是稳定的,但每次调用它(比如memoizedFnCreator(id))都会返回一个全新的函数实例,这就导致传给ChildComponent的onClick props每次渲染都变,哪怕你给子组件套了React.memo也会触发不必要的重渲染。下面给你几个实用的解决方案:

方案一:用useMemo缓存每个id对应的函数集合

我们可以提前为数组里的每个id生成稳定的函数引用,把这些函数存在一个对象里,确保每个id对应的函数引用不会随父组件渲染而变化:

import React, { useCallback, useMemo } from 'react';

const MyComponent = ({ dependancies, anArray }) => {
  // 先定义核心的点击处理逻辑,用useCallback确保自身引用稳定
  const handleClick = useCallback((id) => {
    /* 这里写你的点击逻辑,比如根据id执行对应操作 */
    console.log(`处理id为${id}的点击事件`);
  }, [dependancies]);

  // 用useMemo为每个id生成稳定的函数引用
  const memoizedClickHandlers = useMemo(() => {
    return anArray.reduce((handlerMap, { id }) => {
      handlerMap[id] = () => handleClick(id);
      return handlerMap;
    }, {});
  }, [anArray, handleClick]); // 依赖数组只有在anArray或handleClick变化时才更新

  return (
    <div>
      {anArray.map(({ id }) => (
        <ChildComponent 
          key={id} 
          onClick={memoizedClickHandlers[id]} 
        />
      ))}
    </div>
  );
};

// 别忘了给子组件加上React.memo,确保props不变时不重渲染
const ChildComponent = React.memo(({ onClick }) => {
  console.log('子组件渲染');
  return <button onClick={onClick}>点击我</button>;
});

这个方案的核心是利用useMemo把每个id对应的函数缓存起来,只有当anArray或者handleClick本身变化时,才会重新生成整个handler集合,保证了传给子组件的onClick引用稳定。

方案二:让子组件接收id和通用处理函数,而非绑定后的函数

换个思路,父组件不用提前绑定id到函数上,而是把id和通用的处理函数一起传给子组件,让子组件内部完成绑定:

import React, { useCallback } from 'react';

const MyComponent = ({ dependancies, anArray }) => {
  // 通用的点击处理函数,用useCallback保证引用稳定
  const handleItemClick = useCallback((id) => {
    /* 处理逻辑 */
    console.log(`处理id为${id}的点击事件`);
  }, [dependancies]);

  return (
    <div>
      {anArray.map(({ id }) => (
        <ChildComponent 
          key={id} 
          itemId={id} 
          onItemClick={handleItemClick} 
        />
      ))}
    </div>
  );
};

// 子组件用React.memo包裹,props不变则不重渲染
const ChildComponent = React.memo(({ itemId, onItemClick }) => {
  console.log('子组件渲染');
  return <button onClick={() => onItemClick(itemId)}>点击我</button>;
});

这里父组件传给子组件的onItemClick是稳定的,itemId也是数组项里的固定值,所以子组件的props不会随父组件渲染而变化。虽然子组件内部的onClick每次渲染都会生成新函数,但因为React.memo的存在,只要props不变,子组件就不会重渲染。

方案三:封装自定义Hook复用缓存逻辑

如果你的项目里经常遇到这类场景,可以把缓存逻辑封装成自定义Hook,让代码更简洁易复用:

import React, { useCallback, useMemo } from 'react';

// 自定义Hook:为数组项生成稳定的处理函数集合
const useMemoizedItemHandlers = (items, handler, dependencies) => {
  const stableHandler = useCallback(handler, dependencies);
  
  return useMemo(() => {
    return items.reduce((handlerMap, { id }) => {
      handlerMap[id] = () => stableHandler(id);
      return handlerMap;
    }, {});
  }, [items, stableHandler]);
};

// 组件中使用
const MyComponent = ({ dependancies, anArray }) => {
  const memoizedClickHandlers = useMemoizedItemHandlers(
    anArray,
    (id) => {
      /* 处理逻辑 */
      console.log(`处理id为${id}的点击事件`);
    },
    [dependancies]
  );

  return (
    <div>
      {anArray.map(({ id }) => (
        <ChildComponent 
          key={id} 
          onClick={memoizedClickHandlers[id]} 
        />
      ))}
    </div>
  );
};

const ChildComponent = React.memo(({ onClick }) => {
  console.log('子组件渲染');
  return <button onClick={onClick}>点击我</button>;
});

关键提醒

无论用哪个方案,都必须给ChildComponent加上React.memo,否则就算props不变,子组件还是会随父组件渲染而重新渲染,memoize的效果就白费了。

内容的提问来源于stack exchange,提问作者codingedward

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 16:22:48