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

React点击按钮修改文本后通过setTimeout恢复原值问题

问题根因

你的代码逻辑存在3个明显问题:

  • setTimeout回调里直接执行setText(initialState)会把整个按钮列表全量重置为初始值,不是仅恢复当前点击的那一个。如果2秒等待窗口内点击了多个按钮,先到时间的定时器会把还没到恢复时间的其他按钮也提前改回初始文案。
  • 对数组做浅拷贝后直接修改内部对象的属性、setTimeout回调依赖外层闭包捕获的state,连续快速点击时很容易拿到过期的状态值,出现状态覆盖、文案错乱。
  • 列表渲染用数组下标index作为key,状态更新时可能触发非预期的DOM复用,导致渲染异常,应该用数据自带的唯一id作为key。
修复方案

核心改动是把全量重置状态的逻辑调整为仅更新当前操作的按钮项,同时用函数式写法更新state,规避闭包拿到旧值的问题,修复后的完整代码如下:

import React, { useState } from 'react';

// 用工厂函数返回初始状态,避免意外修改组件外的常量源数据
const getInitialState = () => [
  { id: 0, text: 'add to cart' },
  { id: 1, text: 'add to cart' },
  { id: 2, text: 'add to cart' },
];

const Test: React.FC = () => {
  const [buttonList, setButtonList] = useState(getInitialState);

  const handleClick = (targetId: number) => {
    // 函数式更新拿到最新状态,避免闭包旧值问题,仅修改当前点击的按钮文案
    setButtonList(prev => prev.map(item => 
      item.id === targetId ? { ...item, text: 'Added to cart...' } : item
    ));

    setTimeout(() => {
      // 超时后仅恢复当前点击的按钮,不改动其他按钮的状态
      setButtonList(prev => prev.map(item => 
        item.id === targetId ? { ...item, text: 'add to cart' } : item
      ));
    }, 2000);
  };

  return (
    <div>
      {buttonList.map(button => (
        // 用数据自带的唯一id作为key,替代数组下标
        <button key={button.id} onClick={() => handleClick(button.id)}>
          {button.text}
        </button>
      ))}
    </div>
  );
};

export default Test;
可选优化

如果需要避免用户快速连点同一个按钮导致多个定时器排队重复触发,可以用useRef存储每个按钮对应的定时器id,每次点击前先清除该按钮之前绑定的定时器,保证只有最后一次点击的恢复逻辑生效。


内容的提问来源于stack exchange,提问作者danipereiracodes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:33:16