React中动态加载元素如何实现同一时间仅单个添加active类
问题原因
当前实现存在两个核心问题导致多卡片同时处于选中态:
- 每个
VetItem组件独立维护自身的选中状态,组件之间没有状态同步逻辑 - 直接通过原生DOM方法
classList.toggle修改元素样式类,属于React反模式,UI渲染脱离了状态管控
修复方案
将选中状态上提到渲染所有医生卡片的父组件统一维护,仅存储当前选中的医生唯一ID,通过props将选中状态和点击回调下发给每个VetItem,保证同一时间最多只有一个卡片处于选中态。
1. 父组件改造
在遍历渲染所有医生卡片的父组件中,新增状态存储当前选中的医生ID:
// 医生列表父组件 import { useState, useEffect } from 'react'; import axios from 'axios'; import VetItem from './VetItem'; const DoctorList = () => { // 全局维护当前选中的医生ID,默认无选中 const [selectedVetId, setSelectedVetId] = useState(null); const [vetList, setVetList] = useState([]); const userId = { activeUser: sessionStorage.getItem('activeUser') }; // 把原来写在VetItem里的拉取医生列表逻辑移到父组件,避免每个卡片发重复请求 useEffect(() => { axios .post('http://localhost:80/project-api/readActiveDoctor.php', userId) .then((res) => { setVetList(res.data); }) .catch((err) => { console.log(err); }); }, []); return ( <div className="vet-list"> {vetList.map((item) => ( <VetItem key={item.id} uniqueId={item.id} name={item.name} surname={item.surname} specialization={item.specialization} age={item.age} gender={item.gender} email={item.email} contact={item.phoneNumber} doctorId={item.doctorId} room={item.room} // 下发选中状态和选中回调 isSelected={selectedVetId === item.id} onSelect={() => setSelectedVetId(item.id)} /> ))} </div> ); }; export default DoctorList;
如果需要支持点击已选中卡片取消选中,把onSelect回调替换为以下逻辑即可:
onSelect={() => setSelectedVetId(prev => prev === item.id ? null : item.id)}
2. VetItem组件改造
删除组件内部独立维护的选中状态、直接操作DOM的类名切换逻辑、重复的列表拉取逻辑,完全通过props驱动UI渲染:
const VetItem = (props) => { return ( <div> <button onClick={props.onSelect} // 根据传入的选中状态判断是否加高亮类,完全由状态控制 className={`individual-vet ${props.isSelected ? 'bg-salmon' : ''}`} id={props.uniqueId} > <div className='vet-block-img'> <img className='profileImg vet' src={dp} alt="医生头像" /> </div> <div className='vet-block-text'> <h2>Dr. {props.name + ' ' + props.surname}</h2> <h4>{props.specialization}</h4> </div> </button> </div> ); }; export default VetItem;
优化说明
- 修复后所有卡片的选中状态由父组件统一管控,同一时间只会有一个卡片匹配选中ID,从根源避免多卡片同时高亮的问题
- 移除了直接操作DOM的反模式实现,遵循React状态驱动UI的设计原则
- 把重复的医生列表接口请求移到父组件,避免N个卡片发送N次重复请求,减少性能浪费
内容的提问来源于stack exchange,提问作者Alexa Pettitt
相关产品推荐
相关产品推荐

