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

React记忆卡牌游戏尝试次数计数器异常问题求助

问题分析与解决方案

一、计数器有时加2的原因

你在setSelection的状态更新回调内部调用了setAttempts,如果项目开启了React严格模式(Create React App等脚手架默认开启),React会为检测副作用重复执行状态更新函数,导致setAttempts被调用两次,计数器因此异常累加。

另外,possibleElements和gameTable定义在组件外部,属于全局变量,当组件重新挂载时,possibleElements已被splice清空,会导致后续生成的游戏表格数据异常,这是潜在的隐性bug。

二、抽离计数函数后按钮无法保持选中的原因

在setSelection的回调中调用increaseAttempts(本质是触发attempts状态更新),会在selection状态更新的同时触发组件重渲染,导致Button组件的isSelected属性依赖的selection还未更新为新值,因此无法保持选中状态。

三、修复方案

1. 用useEffect监听selection变化实现计数

将尝试次数的累加逻辑从setSelection回调中移出,通过useEffect监听selection的长度变化,避免在状态更新函数内部触发其他状态更新:

import { useState, useEffect } from 'react';
import Button from "./Button";

export default function GameBoard() {
    const [selection, setSelection] = useState([]);
    const [coupledElements, setCoupledElements] = useState([]);
    const [isEnabled, setIseEnabled] = useState(true);
    const [attempts, setAttempts] = useState(0);

    // 监听selection变化,长度为2时累加尝试次数
    useEffect(() => {
        if (selection.length === 2) {
            setAttempts(prev => prev + 1);
        }
    }, [selection]);

    function sortCoupledElements(selection) {
        if (selection[0].value === selection[1].value) {
            setCoupledElements((prevCoupledElements) => {
                const newCoupledElements = [...prevCoupledElements, selection[0].index, selection[1].index];
                return newCoupledElements;
            })
        }
    }

    function whenClicked(index, value) {
        setSelection((prevSelection) => {
            const newSelection = [...prevSelection, { index, value }];

            if (newSelection.length === 2) {
                setIseEnabled(false);
                sortCoupledElements(newSelection);
                setTimeout(() => {
                    setSelection([]);
                    setIseEnabled(true);
                }, 1000);
            }
            return newSelection;
        })
    }

    // 其余代码保持不变
}

2. 修复全局变量导致的游戏数据异常

把游戏表格的生成逻辑移到组件内部,用useMemo缓存,确保只在组件挂载时生成一次,避免全局变量带来的数据污染:

import { useState, useEffect, useMemo } from 'react';
import Button from "./Button";

export default function GameBoard() {
    // ... 状态定义保持不变

    // 用useMemo缓存游戏表格,仅组件挂载时生成一次
    const gameTable = useMemo(() => {
        const possibleElements = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
        function randomNumber() {
            const randIndex = Math.floor(Math.random() * possibleElements.length);
            const randomNumber = possibleElements[randIndex];
            possibleElements.splice(randIndex, 1);
            return randomNumber;
        }
        return [
            [randomNumber(), randomNumber(), randomNumber(), randomNumber()],
            [randomNumber(), randomNumber(), randomNumber(), randomNumber()],
            [randomNumber(), randomNumber(), randomNumber(), randomNumber()],
            [randomNumber(), randomNumber(), randomNumber(), randomNumber()]
        ];
    }, []);

    // ... 其余代码保持不变
}

3. 额外优化:简化逻辑避免冗余操作

可以把配对、定时器逻辑也移到useEffect中,让代码结构更清晰,同时避免重复操作:

useEffect(() => {
    if (selection.length === 2) {
        setAttempts(prev => prev + 1);
        setIseEnabled(false);
        
        // 处理元素配对
        if (selection[0].value === selection[1].value) {
            setCoupledElements(prev => [...prev, selection[0].index, selection[1].index]);
        }
        
        // 1秒后清空选择并恢复按钮可用状态
        const timer = setTimeout(() => {
            setSelection([]);
            setIseEnabled(true);
        }, 1000);
        
        // 组件卸载时清理定时器
        return () => clearTimeout(timer);
    }
}, [selection]);

// 简化点击事件函数,增加防重复点击逻辑
function whenClicked(index, value) {
    if (!isEnabled || selection.some(item => item.index === index) || coupledElements.includes(index)) {
        return;
    }
    setSelection(prev => [...prev, { index, value }]);
}

修改后,计数器逻辑完全独立于selection的更新流程,不会再出现异常累加的情况,同时按钮的选中状态也能正常保持。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 16:47:10