当前位置: 首页 > news >正文

为何 React JSX 循环需要使用 key

key 是 React 用于识别列表中哪些子元素被改变、添加或删除的唯一标识符
它帮助 React 更高效、更准确地更新和重新渲染列表

1、核心原因:Diff算法与性能优化

React 的核心思想之一是通过虚拟 DOM (Virtual DOM) 来减少对真实 DOM 的直接操作,从而提升性能。当组件的状态或属性发生变化时,React 会创建一个新的虚拟 DOM 树,并将其与之前的虚拟 DOM 树进行比较。这个过程叫做 “Diffing”(差异比对)

假设:在处理列表时候,没有key(React的diffing 算法会变更很低效),默认索引为key作为标识,
原本有一个列表[‘A’,‘B’,‘C’,‘D’],现在需要再原来的基础上在开头新增一个元素 变为 [‘E’,‘A’,‘B’,‘C’,‘D’]

如没有key:
在这里插入图片描述
React 会比较第一个位置的 A 和 X,发现内容变了,于是更新第一个

  • 的文本。接着比较第二个位置的 B 和 A,又更新…以此类推。它会修改所有已有的子元素,而不是意识到 A, B, C, D 只是位置后移了,最终还会在末尾创建一个新的
  • 。这导致了大量不必要的真实 DOM 操作(更新),性能极差
  • 有key时:
    在这里插入图片描述

    React 通过 key(例如 key={item.id})知道 A, B, C ,D 仍然是原来的项,只是移动了位置。它只会创建一个新的

  • (对应 X)并插入到开头,然后移动原有的三个
  • 。这个过程(移动和插入)远比更新每个节点的内容要高效。
  • 2、保持状态的正确性(这是更重要的原因!)
    key 不仅仅是性能优化,它直接关系到组件实例状态的正确性。

    React 使用 key 来匹配多次渲染间的组件实例。如果 key 保持不变,React 会认为这是同一个组件实例,因此它会保留该组件的状态(如 useState、useRef 的值)。如果 key 改变了,React 会销毁旧的组件实例并创建一个新的

    看一个经典的例子:一个待办事项列表,每个事项有一个输入框可以编辑。

    // 错误的做法:使用 index 作为 key
    const TodoList = () => {const [todos, setTodos] = useState(['Learn React', 'Build a project']);const addTodo = () => {const newTodo = prompt('New todo:');// 在列表开头添加新项setTodos([newTodo, ...todos]);};return (<div><button onClick={addTodo}>Add Todo</button><ul>{todos.map((todo, index) => (// 🚨 危险!使用 index 作为 key<li key={index}>{todo}<input type="text" placeholder="Edit..." /></li>))}</ul></div>);
    };
    

    会发生什么?

    初始有两个待办项:Learn React 和 Build a project。它们的 key 分别是 0 和 1。

    你在 第二项 (key=1) 的输入框里输入了 “test”。

    你点击 “Add Todo”,在开头添加了一个新项 “New Item”。

    现在列表变成了:[‘New Item’, ‘Learn React’, ‘Build a project’]。

    React 开始 Diffing:

    旧 key=0 (‘Learn React’) vs 新 key=0 (‘New Item’) -> 内容变了,更新文本,但输入框实例被复用。

    旧 key=1 (‘Build a project’) vs 新 key=1 (‘Learn React’) -> 内容变了,更新文本,输入框实例被复用(现在它显示之前输入的 “test”!)。

    创建一个新的 key=2 的项 (‘Build a project’)。

    结果:你原本在第二项输入的 “test”,神奇地“跳”到了现在第二项(‘Learn React’)的输入框里!这显然不是用户想要的行为。

    如何修复?

    // 正确的做法:使用唯一的 id 作为 key
    {todos.map((todo) => (<li key={todo.id}> {/* 假设每个 todo 对象都有唯一的 id */}{todo.text}<input type="text" placeholder="Edit..." /></li>
    ))}
    
http://www.xdnf.cn/news/1440307.html

相关文章:

  • 一文弄懂C/C++不定参数底层原理
  • Zygote 进程启动流程
  • 视频判重需求:别为同一内容花两次钱!
  • 涨了一倍多的顺丰同城,还能继续做大即时零售基建的蛋糕吗?
  • HTML5 标题标签、段落、换行和水平线
  • 光谱相机的探测器类型
  • 相机在两个机械臂上安装方式比较
  • 字节跳动后端 一面凉经
  • 单片机:GPIO、按键、中断、定时器、蜂鸣器
  • 知微传感Dkam系列3D相机SDK例程篇:CSharp连接相机及保存数据
  • Debezium日常分享系列之:Debezium 3.3.0.Alpha2发布
  • Gemini CLI源码解析:Agent与上下文管理实现细节
  • Airsim 笔记:Python API 总结
  • ESXI8多网卡链路聚合
  • 渗透测试中的常见误区与最佳实践
  • 【LeetCode 热题 100】72. 编辑距离——(解法一)记忆化搜索
  • DBSCAN 密度聚类分析算法
  • 【ProtoBuf 】C++ 网络通讯录开发实战:ProtoBuf 协议设计与 HTTP 服务实现
  • 构建下一代互联网:解码Web3、区块链、协议与云计算的协同演进
  • 【微信小程序预览文件】(PDF、DOC、DOCX、XLS、XLSX、PPT、PPTX)
  • 机器学习进阶,一文搞定模型选型!
  • 智能高效内存分配器测试报告
  • 根据fullcalendar实现企业微信的拖动式预约会议
  • Linux 用户的 Windows 改造之旅
  • Web端最强中继器表格元件库来了!55页高保真交互案例,Axure 9/10/11通用
  • 使用langgraph创建工作流系列3:增加记忆
  • 100种高级数据结构 (速查表)
  • 【NVIDIA B200】1.alltoall_perf 单机性能深度分析:基于 alltoall_perf 测试数据
  • 如何评价2025年数学建模国赛?
  • Debezium系列之:Flink SQL消费Debezium数据,只消费新增数据,过滤掉更新、删除数据