React map循环中点击单个项切换开合全列表生效的解决方法
问题原因
你当前所有列表项共用同一个open布尔状态,只要修改这个状态,所有绑定了该状态的列表项都会同步响应,自然会出现点击任意项全部同步变更的问题。
解决方案
根据你的业务需求可以选择以下两种实现方案:
方案1:允许同时展开多个列表项
将state改为存储键值对对象,键为列表项的唯一标识,值为对应项的开合状态:
import React, { useState } from 'react'; // 初始化state为空对象,默认所有项都是关闭状态 const [openMap, setOpenMap] = useState({}); { FilterAvailable.map((item, index) => { // 优先用item自身的业务唯一id作为标识,没有的话可临时用index const itemKey = item.id ?? index; // 读取当前项的开合状态,默认false关闭 const isOpen = openMap[itemKey] ?? false; return ( <li className="filterItem" key={itemKey}> <div className="filterBtn flex align-center justify-between" onClick={() => { setOpenMap(prev => ({ ...prev, // 仅修改当前点击项的状态 [itemKey]: !isOpen })) }} > <h2>{item.catogery}</h2> <div> {isOpen ? ( <IoIosArrowDown className="downArrow" /> ) : ( <IoIosArrowUp className="downArrow" /> )} </div> </div> {item.options.map(option => { return ( <React.Fragment key={option}> {isOpen && ( <ul className="filterOption"> <li className="filterOptionitem"> <button>{option}</button> </li> </ul> )} </React.Fragment> ); })} </li> ); }); }
方案2:同一时间仅允许展开一个列表项(互斥展开)
将state改为存储当前展开项的唯一标识,默认设为null代表所有项都关闭:
import React, { useState } from 'react'; // 初始化无展开项 const [activeOpenId, setActiveOpenId] = useState(null); { FilterAvailable.map((item, index) => { const itemKey = item.id ?? index; const isOpen = activeOpenId === itemKey; return ( <li className="filterItem" key={itemKey}> <div className="filterBtn flex align-center justify-between" onClick={() => { // 点击已展开的项则关闭,否则切换为当前点击项展开 setActiveOpenId(isOpen ? null : itemKey) }} > <h2>{item.catogery}</h2> <div> {isOpen ? ( <IoIosArrowDown className="downArrow" /> ) : ( <IoIosArrowUp className="downArrow" /> )} </div> </div> {item.options.map(option => { return ( <React.Fragment key={option}> {isOpen && ( <ul className="filterOption"> <li className="filterOptionitem"> <button>{option}</button> </li> </ul> )} </React.Fragment> ); })} </li> ); }); }
注意:如果你的
FilterAvailable列表会有动态新增、删除、排序的操作,不要用map遍历的index作为itemKey,必须使用每个item自身稳定的唯一业务id作为标识,避免出现状态错乱的问题。
内容的提问来源于stack exchange,提问作者Zeeshan
相关产品推荐
相关产品推荐

