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

React.memo失效:父组件更新时子组件仍重复渲染问题

问题描述

子组件已通过React.memo()导出,预期父组件更新时子组件不会重新渲染,但实际父组件更新时,子组件仍会渲染(控制台打印"Children Render")。如何阻止父组件更新时子组件的不必要渲染?

父组件代码

import React, { useState } from "react";
import Children from "./Children";

function Parent() {
  const [name, setName] = useState("");
  const [products, setProducts] = useState([{ name: "", price: "" }]);

  console.log("parent render");
  function handleNameChange(event) {
    setName(event.target.value);
  }

  function handleProductChange(index, product) {
    setProducts((prev) => {
      const newProducts = [...prev];
      newProducts[index] = product;
      return newProducts;
    });
  }

  function addAnotherProduct() {
    setProducts((prev) => {
      const newProducts = prev;
      return [...newProducts, { name: "", price: "" }];
    });
  }

  return (
    <>
      <label>Category:</label>
      <input type="text" value={name} name="name" onChange={handleNameChange} />
      <br />
      {products.map((product, index) => {
        return (
          <Children
            key={index}
            name={product.name}
            price={product.price}
            handleProductChange={(product) =>
              handleProductChange(index, product)
            }
          />
        );
      })}

      <button onClick={addAnotherProduct}>Add another product</button>
    </>
  );
}

export default Parent;

子组件代码

import React from "react";

function Children(props) {
  const productName = props.name;
  const productPrice = props.price;

  const handleChange = (name, value) => {
    const newProduct = {
      name: productName,
      price: productPrice,
      [name]: value
    };
    console.log(newProduct);
    props.handleProductChange(newProduct);
  };

  console.log("Children Render");
  return (
    <>
      <div>
        <label>Product Name:</label>
        <input
          type="text"
          value={productName}
          onChange={(event) => handleChange("name", event.target.value)}
        />
        <br />
        <label>Product Price:</label>
        <input
          type="text"
          value={productPrice}
          onChange={(event) => handleChange("price", event.target.value)}
        />
      </div>
    </>
  );
}

export default React.memo(Children);

已尝试使用React.memo(Children),并为父组件的handleProductChange()添加useCallback(),但均未解决问题。


解决方案

问题核心在于:父组件每次渲染时,都会给子组件传递新的匿名函数(product) => handleProductChange(index, product)——哪怕外层的handleProductChange用了useCallback,这个包裹的匿名函数还是会在每次父组件渲染时重新创建,导致React.memo的浅比较认为props发生变化,触发子组件重渲染。

具体修复步骤:

  1. 用useCallback缓存父组件的handleProductChange函数
    确保该函数在父组件渲染时不会被重新创建,依赖项仅保留稳定的setProducts(React中useState返回的 setter 函数天生稳定,不会随组件渲染变化)。

  2. 直接传递缓存后的函数给子组件,同时将index作为独立props传入
    避免在map中创建新的匿名函数,让子组件自己携带index参数调用父函数。

  3. 修改子组件逻辑,调用时传入index和更新后的product

修改后的代码:

父组件

import React, { useState, useCallback } from "react";
import Children from "./Children";

function Parent() {
  const [name, setName] = useState("");
  const [products, setProducts] = useState([{ name: "", price: "" }]);

  console.log("parent render");
  function handleNameChange(event) {
    setName(event.target.value);
  }

  // 用useCallback缓存函数,依赖项为空数组(setProducts是稳定的)
  const handleProductChange = useCallback((index, product) => {
    setProducts((prev) => {
      const newProducts = [...prev];
      newProducts[index] = product;
      return newProducts;
    });
  }, []);

  function addAnotherProduct() {
    setProducts((prev) => {
      return [...prev, { name: "", price: "" }];
    });
  }

  return (
    <>
      <label>Category:</label>
      <input type="text" value={name} name="name" onChange={handleNameChange} />
      <br />
      {products.map((product, index) => {
        return (
          <Children
            key={index} // 建议后续替换为唯一id,比如给每个product加id字段
            index={index}
            name={product.name}
            price={product.price}
            handleProductChange={handleProductChange}
          />
        );
      })}

      <button onClick={addAnotherProduct}>Add another product</button>
    </>
  );
}

export default Parent;

子组件

import React from "react";

function Children(props) {
  const { name: productName, price: productPrice, index, handleProductChange } = props;

  const handleChange = (field, value) => {
    const newProduct = {
      name: productName,
      price: productPrice,
      [field]: value
    };
    // 调用时传入index和newProduct
    handleProductChange(index, newProduct);
  };

  console.log("Children Render");
  return (
    <>
      <div>
        <label>Product Name:</label>
        <input
          type="text"
          value={productName}
          onChange={(event) => handleChange("name", event.target.value)}
        />
        <br />
        <label>Product Price:</label>
        <input
          type="text"
          value={productPrice}
          onChange={(event) => handleChange("price", event.target.value)}
        />
      </div>
    </>
  );
}

export default React.memo(Children);

验证效果

现在修改父组件的Category输入框时,子组件不会再打印"Children Render",只有当修改对应子组件的产品名称/价格,或者添加新产品时,对应子组件才会重新渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 15:55:03