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

为何React memo包裹的子组件状态在父组件更新时无法持久?

问题原因分析

  1. 闭包导致状态更新覆盖
    父组件的handleClick中直接使用当前list状态生成新列表,但setList是异步更新,连续点击时后续操作会捕获到旧的list状态(函数组件每次渲染都会创建新的handleClick,闭包保留的是当时的list引用),导致之前修改的status被重置为初始值。

  2. memo优化未生效(潜在问题)
    父组件每次渲染都会重新创建handleClick函数,而你的arePropsEqual未对比onClick属性,这会导致即使其他属性无变化,Item组件也会强制重新渲染,违背了memo的性能优化初衷。


修复方案

1. 用函数式更新状态(核心修复)

修改handleClick,使用setList的函数式写法,确保每次更新都基于最新的状态:

const handleClick = (e) => {
  const position = e.currentTarget.id;
  setList(prevList => prevList.map((item, idx) =>
    `${idx}` === position ? { ...item, status: "red" } : item
  ));
};

这种写法会直接拿到最新的prevList,不会覆盖之前的修改。

2. 优化handleClick的稳定性

用useCallback包裹handleClick,避免每次渲染重新创建函数:

import { useCallback } from 'react';

// ...

const handleClick = useCallback((e) => {
  const position = e.currentTarget.id;
  setList(prevList => prevList.map((item, idx) =>
    `${idx}` === position ? { ...item, status: "red" } : item
  ));
}, []); // 依赖为空,因为内部用了函数式更新,无需依赖list

同时更新arePropsEqual,加入onClick的对比,确保memo的判断逻辑完整:

const arePropsEqual = (prevProps, newProps) =>
  prevProps.id === newProps.id &&
  prevProps.idx === newProps.idx &&
  prevProps.status === newProps.status &&
  prevProps.onClick === newProps.onClick;

3. (可选)优化初始列表创建

将初始列表的创建放到组件内部,用useMemo缓存,避免重复生成:

import { useMemo } from 'react';

// ...

const List = () => {
  const initialList = useMemo(() => {
    const randomList = [];
    for (let i = 0; i < 1000; i++) {
      const randomInteger = Math.floor(Math.random() * 100);
      randomList.push({ id: randomInteger, status: "" });
    }
    return randomList;
  }, []);

  const [list, setList] = useState(initialList);
  
  // ... 其他代码
};

总结

核心问题是状态更新未使用函数式写法,闭包捕获旧状态导致修改被覆盖。修复后,每次状态更新都会基于最新的列表数据,之前设置的status将被保留。同时优化handleClick的稳定性,确保memo能真正起到性能优化的作用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 00:08:25