为何React memo包裹的子组件状态在父组件更新时无法持久?
问题原因分析
闭包导致状态更新覆盖
父组件的handleClick中直接使用当前list状态生成新列表,但setList是异步更新,连续点击时后续操作会捕获到旧的list状态(函数组件每次渲染都会创建新的handleClick,闭包保留的是当时的list引用),导致之前修改的status被重置为初始值。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
相关产品推荐
相关产品推荐

