ReactJs中useState管理数组时如何避免组件增删触发全页重渲染
导致全组件重置的核心原因
你的代码有三个致命问题直接触发了异常重挂载:
- 列表
key每次渲染都会重新生成:你在map渲染时直接调用uuid()生成key,React每次对比虚拟DOM时都会发现所有列表项的key和上一次渲染完全不同,会直接判定所有旧组件已销毁,所有新组件需要重新挂载,直接导致所有PostSheet的状态全部重置。 - 错误将JSX实例存入state:state的设计原则是存储可序列化的纯业务数据,把React元素对象存在state里会导致引用匹配混乱,破坏React本身的渲染调度逻辑。
- 删除逻辑完全失效:你在map阶段重新创建了全新的PostSheet实例,和之前存入PostArray的实例引用完全不相等,
indexOf永远匹配不到要删除的目标项,就算key没问题删除功能也跑不通。
修复后代码
直接调整state存储结构,只存每个项的固定唯一标识,渲染阶段再生成组件,key绑定固定ID即可:
import { useState, useCallback } from 'react'; // 导入依赖组件:PostSheet、DefaultLayout、Wrapper export default function Home() { // 仅存储纯数据:每个帖子的固定唯一ID,不存JSX元素 const [postList, setPostList] = useState<Array<{id: string}>>([]); // 新增帖子 function handleClick(event: React.MouseEvent<HTMLButtonElement>) { event.preventDefault(); if (postList.length < 8) { // 新增时生成固定ID,存入state后不会随渲染变化 const newPost = { id: crypto.randomUUID() }; setPostList(prev => [...prev, newPost]); } } // 删除帖子:根据ID过滤,不需要匹配组件引用 const handleRemoveItem = useCallback( (targetId: string) => { // 用函数式更新取最新state,不需要把postList加入依赖 setPostList(prev => prev.filter(item => item.id !== targetId)); }, [] ); return ( <DefaultLayout click={handleClick}> <Wrapper> {postList.map((post) => ( // 绑定固定ID作为key,组件生命周期内key不会变化 <PostSheet key={post.id} id={post.id} click={handleRemoveItem} /> ))} </Wrapper> </DefaultLayout> ); }
注意:需要调整PostSheet组件的click回调,触发时把自身的id传出来,就可以精准匹配删除对应项。
开发规范提醒
- 列表key必须满足「生命周期内稳定、同列表唯一」两个要求,禁止在渲染过程中动态生成随机值当key。
- state只存渲染必需的最小纯数据,JSX元素、DOM实例、函数这类非序列化对象不要直接存在state里。
- 更新列表类state优先用函数式写法,避免闭包缓存旧值,同时可以减少useCallback的依赖项,避免不必要的子组件重渲染。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

