React通过map渲染article列表设置key仍提示子元素需唯一key prop报错
报错核心原因
- React对列表渲染的key要求是:map遍历回调返回的每一个直接根元素必须绑定全局唯一的key值。key的作用是在虚拟DOM diff阶段给节点做身份追踪,React只会检查列表直接子节点的key属性,很多开发者会误以为只要把key写在列表内部的任意子元素上就行,实际上绑定在子节点的子元素上的key完全不会生效。
- 你的代码中
data.map的回调return的最外层是简写的无包裹Fragment(也就是<></>),不是<article>元素——你把key={item.id}写在了Fragment内部的article上,相当于map返回的直接子节点是没有绑定key的空Fragment,完全不满足React的列表渲染要求,因此触发了键值缺失的报错。 - 除了key的报错,代码还存在隐蔽的逻辑问题:展开收起的状态
IsPhidden定义在Question组件的顶层,所有遍历出来的article块共享同一个状态值,后续就算key的问题解决,点击任意一个展开按钮,所有问答块的显示状态都会同步切换。
具体解决步骤
- 修复key绑定逻辑
两种合法的可选写法:- 去掉map回调里包在article外面的那层无意义Fragment,让article成为map返回的直接根元素,把key留在article上即可满足要求;
- 如果确实需要用Fragment包裹多节点,必须使用从react导入的
Fragment组件(简写的<></>不支持绑定key),把key直接绑定在这个Fragment上,比如:import { Fragment } from 'react' // ... {data.map((item) => { return ( <Fragment key={item.id}> <article className='question'> {/* 内部原有内容保持不变 */} </article> </Fragment> ) })}
- 修复状态共享的逻辑bug
把单个问答块的渲染逻辑抽成独立的子组件,把展开收起的状态迁移到这个子组件内部,让每个问答实例维护自己的显示状态。调整后的完整参考代码:import React, { useState } from "react"; import { AiOutlineMinus, AiOutlinePlus } from "react-icons/ai"; // 单个独立的问答块,维护自身展开/收起状态 const QuestionItem = ({ title, info }) => { const [isHidden, setIsHidden] = useState(true); return ( <article className='question'> <header> <h4>{title}</h4> <button onClick={() => setIsHidden(!isHidden)} className='btn' > {isHidden ? <AiOutlinePlus /> : <AiOutlineMinus />} </button> </header> {!isHidden && <p>{info} </p>} </article> ) } const Question = ({ data }) => { return ( <> {data.map((item) => { // key绑定在map直接返回的子组件上,完全符合React要求 return <QuestionItem key={item.id} title={item.title} info={item.info} /> })} </> ); }; export default Question;
内容的提问来源于stack exchange,提问作者Shezad Ahmed
相关产品推荐
相关产品推荐

