You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:33:09