React报列表子元素无唯一key错误但所有子元素均已设置key如何解决
问题成因
1. 列表返回的根Fragment缺少key
你遍历emoteSets.current的map方法中,返回的根节点是React短语法Fragment(<></>)。作为列表渲染的直接子元素,这个Fragment必须添加唯一key,但短语法Fragment不支持传入key属性,会直接触发key缺失警告。
2. 数组索引作为key不可靠
所有列表渲染的key都使用了map的索引参数index,存在两个隐患:
- 多个独立的列表渲染逻辑都用index作为key,不同列表的相同index会出现重复
- 列表数据发生过滤、排序、增减时,index和元素的对应关系会错乱,引发异常
表情的名称本身是唯一值,直接用名称作为key比index更可靠。
3. 列表渲染逻辑不规范
你在map回调中通过if判断决定是否返回元素,不符合条件的项会返回undefined,相当于列表中存在空项,会干扰React的diff判断,建议先对数组做filter过滤,再执行map渲染。
修复方案
核心问题修复(Fragment无key)
首先从React中引入完整的Fragment组件:
import { Fragment } from 'react'
修改遍历emoteSets的代码,给根Fragment添加唯一key:
{showEmotes && // 先过滤再map,避免空返回 Object.keys(emoteSets.current) .filter(key => key !== 'Twitch Global') .map(setKey => { return ( <Fragment key={setKey}> <PopoverHeader>{setKey}</PopoverHeader> <PopoverBody> <Flex flexWrap="wrap"> {emoteSets.current !== null && Object.keys(emoteSets.current[setKey]['emotes']).map(emoteKey => { return ( <ChatEmote key={emoteKey} name={emoteKey} src={ userEmotes[emoteKey][ userOptions.emoteQuality + 'x' ] } height={8} margin={1.5} onClick={() => onClickHandler(emoteKey)} /> ); })} </Flex> </PopoverBody> </Fragment> ); })}
其余列表优化
其他表情列表的渲染也可以参照这个逻辑调整,比如BTTV频道表情的渲染:
{showBTTVChannel && ( <> <PopoverHeader>BTTV Channel Emotes</PopoverHeader> <PopoverBody> <Flex flexWrap="wrap"> {bttvEmotes !== null && // 先过滤分类,再渲染 Object.keys(bttvEmotes) .filter(key => bttvEmotes[key]['category'] === 'bttv_channel') .map(emoteKey => { return ( <ChatEmote key={emoteKey} name={emoteKey} src={ bttvEmotes[emoteKey][userOptions.emoteQuality + 'x'] } height={8} margin={1.5} onClick={() => onClickHandler(emoteKey)} /> ); })} </Flex> </PopoverBody> </> )}
所有列表都替换成用表情唯一名称作为key、先过滤后map的写法,即可完全消除key相关警告。
内容的提问来源于stack exchange,提问作者sslanec
相关产品推荐
相关产品推荐

