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

React中使用useMemo后子组件仍意外重渲染如何解决

问题根因

点击按钮触发所有子组件重渲染,本质是两个写法问题:

  • 父组件每次items状态更新触发重渲染时,内部的handleClick函数都会重新创建,引用地址每次都不一样
  • 子组件错误使用useMemo缓存渲染结果,useMemo本身是用于缓存计算值的API,无法替代组件级别的重渲染拦截,且因为依赖的handleClick每次都变,缓存直接失效

修复方案

分两步修改即可,修改后只有状态实际变化关联的组件会触发重渲染,核心功能完全保留:

  1. 父组件用useCallback包裹handleClick,内部全程使用函数式状态更新,移除对外部items变量的依赖,保证handleClick引用永久稳定
  2. 子组件移除内部错误的useMemo写法,改用React.memo包裹组件,实现props不变时跳过重渲染

修正后的父组件代码

import { useState, useEffect, useCallback } from 'react';
import ButtonDiv from './ButtonDiv';

export default function App() {
  const [items, setItems] = useState([]);

  const handleClick = useCallback((number) => {
    setItems((prevState) => {
      return prevState.includes(number)
        ? prevState.filter((slot) => slot !== number)
        : [...prevState, number]
    })
  }, []);

  useEffect(() => {
    console.log("items", items);
  }, [items]);

  return (
    <div className="App">
      <ButtonDiv handleClick={handleClick} slot="5" />
      <ButtonDiv handleClick={handleClick} slot="10" />
      <ButtonDiv handleClick={handleClick} slot="15" />
      <ButtonDiv handleClick={handleClick} slot="20" />
      <ButtonDiv handleClick={handleClick} slot="25" />
    </div>
  );
}

修正后的子组件代码

import { memo } from 'react';

const ButtonDiv = memo(({ handleClick, slot }) => {
  console.log("renderButton", slot);
  return <button onClick={() => handleClick(slot)}>Click me</button>;
});

export default ButtonDiv;

补充说明
  • useCallback依赖数组为空不会导致状态更新拿到旧值:函数式setState的入参prevState永远是最新的状态快照,不需要将items加入依赖,因此handleClick在组件整个生命周期内引用地址不会发生变化
  • 额外优化建议:当前传入的slot是字符串类型,如果需要存储数字类型的选中值,可以在传参时直接写slot={5},比较时统一使用严格相等===,避免隐式类型转换带来的潜在问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:06:26