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

React+Firebase中数组持续复制引发BUG的问题求助

问题分析与修复方案

核心错误点

  • 数组嵌套问题:toggleActive函数中更新appState时,错误使用[...appState, arrayCopy],将修改后的完整数组作为单个元素追加到原数组中,导致数组嵌套层级不断加深。
  • 状态更新逻辑冗余:setDisabled通过遍历调用toggleActive触发多次状态更新,且依赖旧的appState快照,容易引发状态不同步。
  • 计时器重置逻辑隐患:setTimeLeft回调中setDisabled() || 15的写法依赖无返回值的函数,逻辑不严谨。
  • 样式判断索引不匹配:toggleActiveStyles和toggleActiveStylesBtns用selectedItems的索引去匹配appState元素,但selectedItems是随机筛选的,索引无法对应正确元素。
  • 初始selectedItems无效:初始化selectedItems时appState为空,导致初始值错误。

修复后的代码

const CurrentEventsItem = () => {
    const [timeLeft, setTimeLeft] = useState(15);
    const [appState, changeState] = useState([]);
    const [selectedItems, setSelectedItems] = useState([]);
  
    const getPadTime = (time) => time.toString().padStart(2, "0");
    const minutes = getPadTime(Math.floor(timeLeft / 60));
    const seconds = getPadTime(timeLeft - minutes * 60);

    // 计时器逻辑:到期后重置状态并恢复计时
    useEffect(() => {
        const interval = setInterval(() => {
          setTimeLeft((prevTime) => {
            if (prevTime >= 1) {
              return prevTime - 1;
            } else {
              setDisabled();
              return 15;
            }
          });
        }, 1000);
        return () => clearInterval(interval);
      }, []);
    
    // 随机筛选元素(先复制原数组再打乱,避免修改原数据)
    const getRandomElements = (array, count) => {
        const shuffled = [...array].sort(() => 0.5 - Math.random());
        return shuffled.slice(0, count);
    };

    // 加载初始数据
    useEffect(() => {
        getCurrentEvents();
      }, []);
    
    const getCurrentEvents = async() => {
        const data = await ProductDataService.getAllCurrentEvents();
        const events = data.docs.map((doc) => ({...doc.data(), id: doc.id, statusItem: doc.data().statusItem ?? false}));
        changeState(events);
        // 数据加载完成后初始化选中项
        setSelectedItems(getRandomElements(events, 3));
    };

    // 计时结束后刷新选中项
    useEffect(() => {
        if (timeLeft === 0 && appState.length > 0) {
          setSelectedItems(getRandomElements(appState, 3));
        }
      }, [timeLeft, appState]);

    // 根据item的状态返回样式(通过id匹配appState中的元素)
    const toggleActiveStyles = (item) => {
        const target = appState.find(o => o.id === item.id);
        return target?.statusItem ? "current__events__hot-price disabled" : "current__events__hot-price";
    };
    
    const toggleActiveStylesBtns = (item) => {
        const target = appState.find(o => o.id === item.id);
        return target?.statusItem ? "current__events__btn-green disabled" : "current__events__btn-green";
    };
    
    // 切换单个项的禁用状态
    const toggleActive = (item) => {
        changeState(prevState => {
            return prevState.map(o => {
                if (o.id === item.id) {
                    return {...o, statusItem: !o.statusItem};
                }
                return o;
            });
        });
    };

    // 恢复所有已禁用的项
    const setDisabled = () => {
        changeState(prevState => {
            return prevState.map(o => {
                if (o.statusItem) {
                    return {...o, statusItem: false};
                }
                return o;
            });
        });
    };

    return (
        <>
        <div className='current__events__wrapper'>
            {selectedItems.map((item) => 
                <div className="current__events__hot-price__item" key={item.id}>
                    <div className={toggleActiveStyles(item)}>
                        <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)} 
                        onClick={() => toggleActive(item)} 
                        disabled={item.statusItem}
                    >
                        MAKE TURN
                    </button> 
                </div> 
            )}
            </div>
        </>
    )
}

关键修复说明

  1. 数组嵌套修复:toggleActive改用函数式更新,直接返回修改后的数组,避免将数组作为元素追加。
  2. 批量状态更新:setDisabled通过一次状态更新完成所有项的恢复,避免多次触发重渲染和状态不同步。
  3. 计时器逻辑优化:拆分重置逻辑,先执行setDisabled再返回15,逻辑更清晰。
  4. 样式判断修正:通过item.id匹配appState中的元素,不再依赖索引,确保样式判断准确。
  5. 初始化逻辑调整:在getCurrentEvents加载完成后再设置selectedItems,避免初始值为空;同时给statusItem设置默认值,防止undefined。
  6. 随机函数优化:打乱数组前先复制原数组,避免修改原appState。

内容的提问来源于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.25 17:48:08