如何创建多层嵌套列表?解决点击展开项全部展开的问题
问题分析与解决方案
你现在碰到的问题很典型——所有可展开的列表项都共用了同一个open状态变量!不管你点击哪一个带expan: true的项,都会触发这个全局状态的切换,结果就是所有可展开项同时展开/收起,这肯定不是你想要的效果。
下面给你两种解决思路,都能完美解决这个问题:
思路1:用状态对象/数组追踪每个项的展开状态
我们可以把单个的open布尔值改成一个对象,用来存储每个可展开项的展开状态(用列表项的index作为key)。这样每个项的状态都是独立的,点击时只会切换对应项的状态。
修改后的AboutSkills组件代码:
const AboutSkills = () => { const classes = Styles() // 初始化空对象,每个index对应一个布尔值,默认都是false(收起状态) const [openStates, setOpenStates] = React.useState({}); // 点击时传入当前项的index,只切换对应项的状态 const handleClick = (index) => { setOpenStates(prevStates => ({ ...prevStates, [index]: !prevStates[index] // 翻转当前项的状态,其他项保持不变 })); }; const outputList = () => ( <> <List style={{color:'white'}} component="nav" className={classes.root} > {listItems.map((item, index) => { if(item.expan) { return <Fragment key={index}> <ListItem button onClick={() => handleClick(index)} className={classes.aboutList}> <ListItemIcon className={classes.aboutIcon}> {item.listIcon} </ListItemIcon> <ListItemText primary={item.listText} /> {/* 根据当前项的状态显示图标 */} {openStates[index] ? <ExpandLess /> : <ExpandMore />} </ListItem> {/* 同样,根据当前项的状态控制展开/收起 */} <Collapse in={openStates[index]} timeout="auto" unmountOnExit> <List component="div" disablePadding> <ListItem button className={classes.nested}> <ListItemIcon> <StarBorder /> </ListItemIcon> <ListItemText classes={{primary:classes.expanText}} primary={item.firstText} /> </ListItem> { item.secondText && ( <ListItem button className={classes.nested}> <ListItemIcon> <StarBorder /> </ListItemIcon> <ListItemText classes={{primary:classes.expanText}} primary={item.secondText} /> </ListItem> )} { item.thirdText && ( <ListItem button className={classes.nested}> <ListItemIcon> <StarBorder /> </ListItemIcon> <ListItemText classes={{primary:classes.expanText}} primary={item.thirdText} /> </ListItem> )} </List> </Collapse> </Fragment> } else { return <ListItem button className={classes.aboutList} key={index}> <ListItemIcon className={classes.aboutIcon}> {item.listIcon} </ListItemIcon> <ListItemText classes={{primary:classes.unExpanText}} primary={item.listText} /> </ListItem> } })} </List> </> ) return outputList() }
思路2:把可展开项抽成独立组件(更推荐)
这种方法更符合React的组件化思想,把每个可展开的列表项做成一个独立的组件,让它自己管理自己的open状态,这样各个项之间完全隔离,不会互相影响。
第一步:创建ExpandableListItem组件
const ExpandableListItem = ({ item, classes }) => { // 每个组件自己维护独立的open状态 const [open, setOpen] = React.useState(false); const handleClick = () => { setOpen(!open); }; return ( <Fragment> <ListItem button onClick={handleClick} className={classes.aboutList}> <ListItemIcon className={classes.aboutIcon}> {item.listIcon} </ListItemIcon> <ListItemText primary={item.listText} /> {open ? <ExpandLess /> : <ExpandMore />} </ListItem> <Collapse in={open} timeout="auto" unmountOnExit> <List component="div" disablePadding> {/* 用&&替代三元表达式,代码更简洁 */} {item.firstText && ( <ListItem button className={classes.nested}> <ListItemIcon><StarBorder /></ListItemIcon> <ListItemText classes={{primary:classes.expanText}} primary={item.firstText} /> </ListItem> )} {item.secondText && ( <ListItem button className={classes.nested}> <ListItemIcon><StarBorder /></ListItemIcon> <ListItemText classes={{primary:classes.expanText}} primary={item.secondText} /> </ListItem> )} {item.thirdText && ( <ListItem button className={classes.nested}> <ListItemIcon><StarBorder /></ListItemIcon> <ListItemText classes={{primary:classes.expanText}} primary={item.thirdText} /> </ListItem> )} </List> </Collapse> </Fragment> ); };
第二步:在AboutSkills中使用这个组件
const AboutSkills = () => { const classes = Styles() const outputList = () => ( <> <List style={{color:'white'}} component="nav" className={classes.root} > {listItems.map((item, index) => { if(item.expan) { // 直接渲染独立组件,传入item和classes return <ExpandableListItem key={index} item={item} classes={classes} />; } else { return <ListItem button className={classes.aboutList} key={index}> <ListItemIcon className={classes.aboutIcon}> {item.listIcon} </ListItemIcon> <ListItemText classes={{primary:classes.unExpanText}} primary={item.listText} /> </ListItem> } })} </List> </> ) return outputList() }
额外提一句:你的listItems数组里有两个重复的ReactJs项,虽然不影响功能,但可以清理一下,让代码更整洁~
内容的提问来源于stack exchange,提问作者Junaid Razaq
相关产品推荐
相关产品推荐

