React标签页开发问题:key缺失警告、展开控制及radio button高亮
问题解答:嵌套Tab组件开发中的三个常见问题
场景说明
我有一个用于构建Tab的嵌套对象数组:
export const tabData = [ { id: 1, name:"Population", col: [ { ItemCode: 1001, ItemName: "MalePopulation" }, { ItemCode: 1002, ItemName: "FemalePopulation" }, { ItemCode: 1003, ItemName: "TotalPopulation" }, ], }, { id: 2, name:"Poverty", col: [ { ItemCode: 1004, ItemName: "RuralRationShops" }, { ItemCode: 1005, ItemName: "UrbanRationShops" }, { ItemCode: 1006, ItemName: "TotalRationShops" }, ], }, { id: 3, name:"Agriculture", col: [ { ItemCode: 1007, ItemName: "AgriculturalLand" }, { ItemCode: 1008, ItemName: "NonAgriculturalLand" }, { ItemCode: 1009, ItemName: "TotalLand" }, ], }, ];
开发中遇到三个问题:
- 已为所有元素设置key,但仍触发key缺失警告,原因是什么?
- 点击父元素时所有三组父-子项均展开,如何限制仅展开被点击父节点的子div?
- 如何实现用户点击时高亮radio button,且仅展开对应下方子div?
当前实现代码:
const [open, setOpen] = useState(false); function clickHandler1(e) { setOpen(!open); } <div className="h-42 overflow-y-auto"> {Object.keys(tabdata).map(function (keyName, keyIndex) { const col1 = tabdata[keyName].col; return ( <> <label key={keyIndex} className="flex flex-row"> <button key={keyIndex} onClick={(e) => { clickHandler1(e); }} className="flex flex-row" > <BiArrowFromTop /> {tabdata[keyName].name} </button> </label> {Object.keys(col1).map(function (keyName1, keyIndex1) { return ( <> {open && ( <lable key={col1[keyName1].ItemCode} className="flex flex-row px-6"> <Link key={col1[keyName1].ItemCode} href={`gismappage?paths=${yrId}/${chpId}/${col1[keyName1].ItemCode}`}> <input name="itm" key={col1[keyName1].ItemCode} type="radio" onClick={() => clickhandler2(col1[keyName1].ItemCode) } /> </Link> {col1[keyName1].ItemName} </lable> )} </> ); })} </> ); })} </div>
问题1:key缺失警告原因
你用了<>(Fragment)包裹循环返回的元素,但没有给Fragment设置key。React要求循环生成的最外层元素必须有唯一key,你现在把key加在了内部的label和button上,最外层的Fragment没加key,所以触发警告。
另外,用数组下标keyIndex当key不推荐,一旦数组元素顺序变化,会导致React复用错误组件,建议用每个tab的id(比如tabdata[keyName].id)当外层循环的key,子循环用ItemCode当key,这两个都是唯一值,比下标更可靠。
修复示例:
// 外层循环的Fragment加key return ( <React.Fragment key={tabdata[keyName].id}> {/* 内部元素 */} </React.Fragment> )
问题2:仅展开被点击的父节点子项
你现在用了一个全局的open布尔状态控制所有子项的显示/隐藏,所有父节点点击都会切换这个状态,自然会全部展开/收起。
解决方法:把状态改成记录当前展开的父节点id,而不是布尔值。初始值设为null或者某个默认展开的id:
// 替换原来的useState const [expandedTabId, setExpandedTabId] = useState(null); // 修改点击事件 function clickHandler1(tabId) { // 如果点击的是当前展开的tab,就关闭;否则展开它 setExpandedTabId(expandedTabId === tabId ? null : tabId); } // 父节点点击时传入当前tab的id <button onClick={() => clickHandler1(tabdata[keyName].id)} className="flex flex-row" > <BiArrowFromTop /> {tabdata[keyName].name} </button> // 子项显示条件改成判断当前tab的id是否等于expandedTabId {expandedTabId === tabdata[keyName].id && ( // 子项内容 )}
这样每个父节点点击只会切换自己的展开状态,不会影响其他tab。
问题3:点击radio高亮并展开对应子div
首先,radio的高亮是原生行为,只要name属性一致,点击后会自动高亮选中项。但你现在的radio放在Link标签里,可能会阻止默认行为,建议把Link包裹整个label,而不是只包input。
另外,要实现点击radio时展开对应父tab,需要在radio的点击事件里同步设置expandedTabId为当前父tab的id:
修改后的子项部分代码:
{Object.keys(col1).map(function (keyName1) { const item = col1[keyName1]; const currentTabId = tabdata[keyName].id; return ( <React.Fragment key={item.ItemCode}> {expandedTabId === currentTabId && ( <label key={item.ItemCode} className="flex flex-row px-6"> <Link href={`gismappage?paths=${yrId}/${chpId}/${item.ItemCode}`}> <input name="itm" type="radio" onClick={() => { clickhandler2(item.ItemCode); // 点击radio时确保当前父tab展开 setExpandedTabId(currentTabId); }} /> {item.ItemName} </Link> </label> )} </React.Fragment> ); })}
另外注意:你写的<lable>是拼写错误,应该是<label>,这个也要修正,避免样式或交互异常。
内容的提问来源于stack exchange,提问作者Jaya
相关产品推荐
相关产品推荐

