React实现对象数组遍历及点击高亮单个列表项问题
解决React列表项点击仅高亮选中项的问题
你遇到的问题很典型——现在你的active状态是一个布尔值,所有列表项都根据这个值来判断是否添加hover类,所以一旦状态切换,所有项都会同时高亮或者取消高亮。要实现“点击哪项哪项高亮,其余保持原样”的效果,我们需要追踪具体选中的项的ID,而不是用一个统一的布尔值。
下面是具体的修改步骤和完整代码:
1. 调整状态结构
把原来的active: false改成selectedId: null,用来存储当前选中项的ID,初始值null表示没有任何项被选中。
2. 更新点击事件处理函数
修改toggleClass方法,点击时判断当前点击的ID是否和selectedId一致:
- 如果一致,说明是点击了已经高亮的项,把
selectedId设为null取消高亮 - 如果不一致,就把
selectedId设为当前点击的ID,让该项高亮
3. 动态判断列表项的类名
在渲染<li>的时候,对比当前项的id和selectedId,相等就添加hover类,否则不添加。
修改后的完整代码
import React, { Component } from 'react'; class MenuSelector extends Component { state = { selectedId: null, // 改为存储选中的ID menu: [ { id: 1, fighterName: 'Goku', race: 'Sayian', specialAttack: 'Kamehameha Wave', img: 'https://nerdist.com/wp-content/uploads/2018/01/Dragon-Ball-Super-Goku.jpg' }, { id: 2, fighterName: 'Vegeta', race: 'Sayian', specialAttack: 'Final Flash', img: 'https://imgmr.com/wp-content/uploads/2016/04/Vegeta-SSGSS-2.png' }, { id: 3, fighterName: 'Gohun', race: 'Human', specialAttack: 'Masenko', img: 'https://techanimate.com/wp-content/uploads/2017/11/Why-does-Gohan-Have-so-much-Hidden-Power-1024x576.jpg' }, { id: 4, fighterName: 'Krillin', race: 'Human', specialAttack: 'Destructo Disc', img: 'https://static3.srcdn.com/wordpress/wp-content/uploads/2016/09/Krillin-Destructo-Disk.jpg' }, { id: 5, fighterName: 'Android 17', race: 'Android', specialAttack: 'Barrier', img: 'https://i.ytimg.com/vi/SzAiIy_Dnb4/maxresdefault.jpg' }, { id: 6, fighterName: 'Jiren', race: 'Unknown', specialAttack: 'Eye Paralysis', img: 'https://vignette.wikia.nocookie.net/dragonuniverse/images/9/9c/Jiren.png/revision/latest/scale-to-width-down/2000?cb=20180211002429' }, { id: 7, fighterName: 'Lord Beerus', race: 'God', specialAttack: 'Hakai', img: 'https://vignette.wikia.nocookie.net/dragonuniverse/images/3/30/Beerus2.png/revision/latest?cb=20170225064338' }, { id: 8, fighterName: 'Cell', race: 'Android', specialAttack: 'Kamehameha Wave', img: 'https://static0.srcdn.com/wordpress/wp-content/uploads/Dragon-Ball-Z-Perfect-Cell.jpg' }, { id: 9, fighterName: 'Frieza', race: 'Unknown', specialAttack: 'Finger Blasters', img: 'https://i.ytimg.com/vi/BkoOw_7JqKg/maxresdefault.jpg' }, { id: 10, fighterName: 'Black Goku', race: 'Sayian', specialAttack: 'Ki Blade', img: 'https://pm1.narvii.com/6287/eafbdf4dc0ae1b58d3869b89be04bcd0712f7623_hq.jpg' }, ] } toggleClass = (id) => { console.log(`Clicked item ${id}`); // 判断当前点击的ID是否是已选中的 this.setState(prevState => ({ selectedId: prevState.selectedId === id ? null : id })); }; render() { return ( <div className="container"> <h1>Menu Selector</h1> <div> <ul> {this.state.menu.map((dragonball) => ( // 注意:这里用dragonball.id作为key比index更好,避免列表更新时的问题 <li key={dragonball.id} onClick={() => this.toggleClass(dragonball.id)} className={this.state.selectedId === dragonball.id ? 'hover' : ''} > <div className="container-dragonball"> <div className="dragonball-stats"> <h1>{dragonball.fighterName}</h1> <p>Race: {dragonball.race}</p> <p>Special Attack: {dragonball.specialAttack}</p> </div> <div className="dragonball-img"> <img src={dragonball.img} alt={dragonball.fighterName} /> </div> </div> </li> ))} </ul> </div> </div> ); } } export default MenuSelector;
额外优化提示
- 我把列表项的
key从index改成了dragonball.id,这是React列表渲染的最佳实践——因为index会随着列表项的增删改变,而id是唯一且稳定的,能避免不必要的组件重渲染和状态混乱。 - 如果你的需求是“必须保持有一个项高亮,不能取消”,那只需要把
toggleClass里的逻辑改成直接设置selectedId: id,去掉判断是否相等的部分即可。
内容的提问来源于stack exchange,提问作者Andrew Baisden
相关产品推荐
相关产品推荐

