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

使用React.memo与useCallback仍出现组件重复渲染问题求助

问题:React.memo与useCallback未能阻止不必要的重复渲染

我在父组件中使用了React.memo和useCallback,想避免点击按钮时组件出现不必要的重复渲染,但每次点击按钮后,控制台仍输出大量重复渲染日志:

Rendering Title
Title.js:4 Rendering Title
Count.js:4 Rendering Age
Count.js:4 Rendering Age
Button.js:4 Rendering button -   Increment Age
Button.js:4 Rendering button -   Increment Age
Count.js:4 Rendering Salary
Count.js:4 Rendering Salary
Button.js:4 Rendering button -  Increment Salary
Button.js:4 Rendering button -  Increment Salary

相关组件代码

父组件代码

import React, { useCallback, useState } from 'react';
import Title from './Title';
import Count from './Count';
import Button from './Button';

function ParentComponent() {
  const [age, setAge] = useState(25);
  const [salary, setSalary] = useState(50000);

  const incrementAge = useCallback(() => {
    setAge(age + 1);
  }, [age]);

  const incrementSalary = useCallback(() => {
    setSalary(salary + 1000);
  }, [salary]);

  return (
    <>
      <Title />
      <Count text="Age" count={age} />
      <Button handleClick={incrementAge}>Increment Age</Button>
      <Count text="Salary" count={salary} />
      <Button handleClick={incrementSalary}>Increment Salary</Button>
    </>
  );
}

export default ParentComponent;

Title.js代码

import React from 'react';

function Title() {
  console.log("Rendering Title");
  return (
    <div>Title</div>
  );
}

export default React.memo(Title);

Count.js代码

import React from 'react';

function Count({ text, count }) {
  console.log(`Rendering ${text}`);
  return (
    <div>{text} - {count}</div>
  );
}

export default React.memo(Count);

Button.js代码

import React from 'react'
//children -- props.children -- Increment age -- 
function Button({handleClick , children}) {
  console.log('Rendering button - ', children)
    return (
    <button onClick={handleClick}>{children}</button>
  )
}

export default React.memo(Button); 

问题原因分析

  1. useCallback依赖项导致回调引用更新:
    当前incrementAge的依赖项包含age,每次age变化时,这个回调函数的引用会重新生成,导致接收它的<Button>Increment Age</Button>组件因为handleClick props变化而重新渲染;同理incrementSalary依赖salary,每次薪资变化时对应按钮也会重新渲染。

  2. React StrictMode的双重渲染机制:
    日志中每个渲染信息重复出现两次,是因为开发环境下开启了React严格模式(StrictMode)。该模式会故意执行两次渲染、副作用函数等操作,用于检测代码中潜在的问题,生产环境下不会有这种重复行为。

  3. 必要的渲染无法避免:
    比如当age变化时,展示年龄的<Count text="Age" count={age} />组件因为count props更新,必然需要重新渲染,这属于正常的业务逻辑渲染,无法也不应该阻止。

解决方案

1. 优化useCallback,使用函数式更新消除依赖

将状态更新改为函数式写法,让useCallback的依赖项为空数组,保证回调函数的引用始终不变,从而避免按钮组件因回调变化而渲染:

const incrementAge = useCallback(() => {
  // 使用函数式更新,获取最新的prev状态
  setAge(prevAge => prevAge + 1);
}, []); // 依赖项为空数组

const incrementSalary = useCallback(() => {
  setSalary(prevSalary => prevSalary + 1000);
}, []);

2. 忽略StrictMode的开发环境重复日志

如果是开发环境下的重复渲染日志,无需担心,这是StrictMode的正常调试行为,生产环境打包后会自动消失。

3. 确认React.memo的生效条件

React.memo只会浅比较组件的props,只要props没有变化,就会阻止组件重新渲染。优化后的回调引用不变,Button组件的props(handleClick和children)都不会变化,React.memo就能正常生效,阻止不必要的渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:33:34