React映射渲染JobCard列表时如何保持选中状态与高亮样式
实现方法
你当前代码里给每个列表项设置的key={job.id}就是最可靠的唯一索引,API返回的岗位id是全局唯一值,不会因为列表排序、增删数据发生变化,完全可以作为选中状态的判定依据,不需要额外生成索引。不要用map遍历的数组下标做标识,否则列表变动时会出现状态错位的问题。
根据你是否需要在父组件获取选中项的数据,有两种实现方式可选:
方案1:父组件统一管理选中状态(推荐)
如果后续需要在父层拿到当前选中的岗位数据做其他逻辑(比如提交选中项、联动其他组件),就把选中状态存在父组件:
- 父组件新增state存储当前选中的岗位id
- 给每个JobCard传入两个props:当前项是否选中的布尔值、切换选中状态的回调
- JobCard根据传入的选中状态控制蓝色高亮样式,点击时触发回调更新父组件state
父组件修改后的代码:
import { useState } from 'react'; // 父组件其他逻辑... const [selectedJobId, setSelectedJobId] = useState(null); const toggleSelectJob = (jobId) => { // 点击已选中的项则取消选中,否则设为当前选中项 setSelectedJobId(prev => prev === jobId ? null : jobId); }; // 列表渲染部分 { jobs.length > 0 && jobs.map((job) => ( <JobCard key={job.id} job={job} isSelected={selectedJobId === job.id} onToggleSelect={() => toggleSelectJob(job.id)} /> )) }
JobCard组件修改后的代码:
import { useState } from 'react'; const JobCard = ({ job, isSelected, onToggleSelect }) => { const responseAdjusted = job.category.label const responseArray = responseAdjusted.split(" ")[0] // 原有收藏按钮状态 const [favoriteOpen, setFavoriteOpen] = useState(false); return ( <CardContainer onClick={onToggleSelect} sx={{ // 按你的设计需求调整选中时的蓝色样式 border: isSelected ? '2px solid #1976d2' : '1px solid #e0e0e0', backgroundColor: isSelected ? '#e3f2fd' : '#fff', cursor: 'pointer' }} > <CardPrimary> <CardHeader> <CardHeaderTopRow> <Typography variant = "cardheader1"> {job.title} </Typography> <HeartDiv> <IconButton color={favoriteOpen ? "error" : "buttoncol"} sx={{ boxShadow: 3}} size="small" > <FavoriteIcon fontSize="inherit" onClick={(e) => { // 阻止事件冒泡,避免点击收藏按钮时触发卡片选中 e.stopPropagation(); setFavoriteOpen(prev => !prev); }} /> </IconButton> </HeartDiv> </CardHeaderTopRow> <Typography variant = "subtitle4" color="text.secondary"> {job.company.display_name} </Typography> </CardHeader> <CardSecondary></CardSecondary> </CardPrimary> </CardContainer> ) }
方案2:子组件独立管理选中状态
如果不需要父组件感知选中状态,直接把选中状态存在每个JobCard内部即可。React会根据你设置的key={job.id}区分不同的卡片实例,每个实例的状态互相独立,不会出现串状态的问题:
import { useState } from 'react'; const JobCard = ({ job }) => { const responseAdjusted = job.category.label const responseArray = responseAdjusted.split(" ")[0] const [favoriteOpen, setFavoriteOpen] = useState(false); // 子组件自己维护选中状态 const [isSelected, setIsSelected] = useState(false); return ( <CardContainer onClick={() => setIsSelected(prev => !prev)} sx={{ border: isSelected ? '2px solid #1976d2' : '1px solid #e0e0e0', backgroundColor: isSelected ? '#e3f2fd' : '#fff', cursor: 'pointer' }} > <CardPrimary> <CardHeader> <CardHeaderTopRow> <Typography variant = "cardheader1"> {job.title} </Typography> <HeartDiv> <IconButton color={favoriteOpen ? "error" : "buttoncol"} sx={{ boxShadow: 3}} size="small" > <FavoriteIcon fontSize="inherit" onClick={(e) => { e.stopPropagation(); setFavoriteOpen(prev => !prev); }} /> </IconButton> </HeartDiv> </CardHeaderTopRow> <Typography variant = "subtitle4" color="text.secondary"> {job.company.display_name} </Typography> </CardHeader> <CardSecondary></CardSecondary> </CardPrimary> </CardContainer> ) }
补充说明:如果需要实现多选效果,把方案1里父组件的
selectedJobId改成数组类型,存储所有选中的jobId,判断选中时用selectedJobId.includes(job.id),切换选中时对应做数组的添加/删除项操作即可。
内容的提问来源于stack exchange,提问作者n_d22
相关产品推荐
相关产品推荐

