React递归Accordion组件报列表子元素需唯一key prop的解决方法
报错原因
这个报错是React列表渲染的通用规则触发的:所有通过数组遍历(一般是Array.prototype.map)生成的元素,必须给遍历返回的最外层根节点传入同层级唯一的key属性,用于虚拟DOM的差异比对优化。
你的代码里有两个和key相关的错误:
- 你加在最外层
<div className={'tabs'}>上的key={explorer.label}完全无效,这个元素不在任何数组遍历的循环体内,React不会把它识别为列表项的key - 真正触发报错的位置是
explorer.children.map的遍历返回值:你返回的最外层节点是<React.Fragment>,这个节点没有设置key属性,React遍历生成列表项时找不到唯一标识就会抛出这个错误。
修复步骤
- 第一步:删除最外层tabs div上多余的无效key属性
- 第二步:给map遍历返回的
React.Fragment加上同层级唯一的key。如果你的数据源每个节点有全局唯一的id字段,优先用id当key;如果没有id,且同层级下label不会重复,可以临时用child.label当key - 注意:给
React.Fragment传key属性时不能用简写语法<></>,必须使用完整的<React.Fragment>标签写法,你当前的写法是支持传key的,直接加属性即可。
修复后完整代码
import React, { useState, useCallback } from 'react' import { Container, Grid } from '@material-ui/core' import './styles.css' function Accordion({ explorer }) { // 修正了原组件名的拼写错误,原拼写Accodian少了个r,不影响运行但不符合常规命名 const [ expand, setExpand ] = useState(false) const handleExpand = useCallback(() => { setExpand(prevState => !prevState) }) if (explorer.children) { return ( // 已删除此处原有的无效key <div style={{ width: '100%' }} className={'tabs'}> {explorer.children ? ( <> <div className='tab' onClick={handleExpand}> <label className={ expand ? 'tab-label-expanded' : 'tab-label' }> {explorer.label} </label> </div> {expand ? ( <Container className='tab-content'> <Grid container spacing={1}> {explorer.children.map(child => { return ( // 给遍历的最外层Fragment加上唯一key <React.Fragment key={child.id ?? child.label}> {Array.isArray(child.children) ? ( <Accordion explorer={child}/> ) : ( <Grid item xs={12} sm={6} md={6} lg={4} xl={3}> <li> <b>{child.label}</b>{' '}: {child.value} </li> </Grid> )} </React.Fragment> ) })} </Grid> </Container> ) : null} </> ) : null} </div> ) } else { return <div style={{ paddingLeft: '20px' }}>{explorer.label}</div> } } export default Accordion
补充说明:如果你的业务场景下同层级可能出现label重复的情况,一定要提前给每个数据节点加全局唯一的id字段,用id作为key可以完全避免列表渲染错位、状态混乱的问题。
内容的提问来源于stack exchange,提问作者KARTHIKEYAN.A
相关产品推荐
相关产品推荐

