React map渲染列表时如何仅禁用被点击按钮及定时器到期恢复类名
问题原因
当前实现使用组件顶层的全局disabled状态绑定所有按钮的禁用属性,所有按钮共享同一个状态值,修改时会同步触发所有按钮的状态变化。同时全局仅维护一个倒计时变量,所有列表项共用同一份计时逻辑,无法支持单个项独立计时、到期自动恢复状态的需求。
修复方案
核心思路是将按钮禁用状态、倒计时时间下沉到每个列表项自身的数据结构中存储,废弃全局独立的控制状态,倒计时到期时直接修改对应项的状态即可自动触发类名切换。
关键调整点
- 移除全局的
disabled、timeLeft状态,给每个列表项新增timeLeft字段存储自身剩余倒计时时长 - 保留一个全局间隔定时器,每秒批量处理所有列表项的倒计时逻辑,时间归零时自动将对应项的
statusItem设为false,类名会自动完成切换 - 按钮的
disabled属性直接绑定当前项自身的statusItem字段,不再依赖全局状态 - 列表渲染时每个项基于自身的
timeLeft单独计算分秒显示值,不再共用全局倒计时 - 列表key改用项自身的唯一id,避免列表重排时出现状态错位问题
- 所有状态更新采用函数式写法,避免闭包引用旧状态导致的更新异常
修改后完整代码
import { useEffect, useState } from 'react' import './CurrentEventsItem.scss' const CurrentEventsItem = () => { const getPadTime = (time) => time.toString().padStart(2, '0') const [appState, changeState] = useState({ objects: [ {id: 1, title: 'Apple iPhone 13 Pro Max 256Gb (небесно-голубой)', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false, timeLeft: 0}, {id: 2, title: '500 Stars', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false, timeLeft: 0}, {id: 3, title: 'Sony PlayStation 5 Digital Edition ', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false, timeLeft: 0} ] }) // 统一批量处理所有项的倒计时 useEffect(() => { const interval = setInterval(() => { changeState(prev => { const updatedObjects = prev.objects.map(item => { if (item.timeLeft <= 0) return item const newTime = item.timeLeft - 1 // 倒计时归零自动重置状态 if (newTime <= 0) { return {...item, timeLeft: 0, statusItem: false} } return {...item, timeLeft: newTime} }) return {...prev, objects: updatedObjects} }) }, 1000) return () => clearInterval(interval) }, []) const toggleActive = (index) => { changeState(prev => { const targetItem = prev.objects[index] // 已禁用状态不重复触发 if (targetItem.statusItem) return prev const updatedObjects = [...prev.objects] updatedObjects[index] = { ...targetItem, statusItem: true, timeLeft: 5 * 60 // 点击时启动5分钟倒计时 } return {...prev, objects: updatedObjects} }) } const toggleActiveStyles = (statusItem) => { return statusItem ? 'current__events__hot-price disabled' : 'current__events__hot-price' } const toggleActiveStylesBtns = (statusItem) => { return statusItem ? 'current__events__btn-green disabled' : 'current__events__btn-green' } return ( <div className='current__events__wrapper'> {appState.objects.map((item, index) => { // 单独计算当前项的分秒显示 const minutes = getPadTime(Math.floor(item.timeLeft / 60)) const seconds = getPadTime(item.timeLeft - minutes * 60) return ( <div className="current__events__hot-price__item" key={item.id}> <div className={toggleActiveStyles(item.statusItem)}> <h5 className="current__events__card-title__large">Hot Price</h5> </div> <div className="current__events__image"> <img src={item.avatar} alt='user' className="rounded-circle" width='75' height='75'/> </div> <div className="current__events__info"> <h4 className="current__events__title__middle">{item.title}</h4> </div> <div className="current__events__timer"> <span>{minutes}</span> <span>:</span> <span>{seconds}</span> </div> <button className={toggleActiveStylesBtns(item.statusItem)} onClick={() => toggleActive(index)} disabled={item.statusItem} > СДЕЛАТЬ ХОД </button> </div> ) })} </div> ) } export default CurrentEventsItem
内容的提问来源于stack exchange,提问作者Eugene Kliuchnikov
相关产品推荐
相关产品推荐

