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

React开发国际象棋游戏:点击移动时activePiece变量置空问题求助

解决方案

核心问题分析

你当前用let activePiece = null这种普通变量管理选中的棋子,React组件每次渲染时这个变量都会被重置;同时点击棋子时mousedown和mouseup会连续触发,导致movePiece立刻把activePiece设为null,无法保留选中状态。必须改用React的useState管理选中状态,同时区分"选中棋子"和"点击目标位置"两种操作。

具体修改步骤

  1. 改用useState管理activePiece
    替换普通变量为React状态,确保选中状态在组件渲染间保留:

    // 替换 let activePiece = null;
    const [activePiece, setActivePieceState] = useState(null);
    
  2. 重写setActivePiece函数
    区分两种场景:已有选中棋子时点击棋盘则触发移动,点击棋子则切换选中状态:

    function setActivePiece(e) {
        e.preventDefault();
        // 点击棋盘:若有选中棋子则移动
        if (e.target.id === "boardImg") {
            activePiece && movePiece(e);
            return;
        }
        // 点击棋子:切换选中状态(点击已选中棋子则取消)
        const clickedPiece = e.target;
        setActivePieceState(prev => 
            prev?.id === clickedPiece.id ? null : clickedPiece
        );
    }
    
  3. 修改movePiece函数
    依赖状态中的activePiece,移动后重置选中状态,且避免直接修改原对象:

    function movePiece(e) {
        if (!activePiece) return;
    
        const boardRect = boardRef.current.getBoundingClientRect();
        const pieceSize = boardRect.width / 8;
        const targetX = parseInt((e.clientX - boardRect.x) / pieceSize);
        const targetY = parseInt((e.clientY - boardRect.y) / pieceSize);
    
        setPieces(prevState => 
            prevState.map(p => 
                p.id === activePiece.id ? {...p, x: targetX, y: targetY} : p
            )
        );
        // 移动完成后清空选中状态
        setActivePieceState(null);
    }
    
  4. 修正useEffect的依赖
    原代码依赖activePiece会导致每次选中棋子都重新生成棋盘,改为仅在组件挂载时初始化:

    useEffect(() => {
        setPieces(generateBoard());
    }, []); // 空依赖数组,仅挂载时执行
    
  5. 调整拖拽函数dragPiece
    改用状态中的activePiece,优化计算逻辑:

    function dragPiece(e){
        if (!activePiece) return;
    
        const boardRect = boardRef.current.getBoundingClientRect();
        const pieceHalfSize = boardRect.width / 4;
        activePiece.style.left = `${e.clientX - boardRect.x - pieceHalfSize}px`;
        activePiece.style.top = `${e.clientY - boardRect.y - pieceHalfSize}px`;
    }
    
  6. 修正Piece组件的key
    不要用uuidv4()作为key(每次渲染都会生成新key,导致组件重复卸载挂载),改用棋子的唯一id:

    pieces.map(piece => <Piece key={piece.id} piece={piece}/>)
    

最终修改后的完整Board组件代码

import { useState, useRef, useEffect } from 'react';
// 假设已导入boardImg、generateBoard等依赖

function Board(){
    const [pieces, setPieces] = useState([]);
    const [activePiece, setActivePieceState] = useState(null);
    const boardRef = useRef();

    function movePiece(e) {
        if (!activePiece) return;

        const boardRect = boardRef.current.getBoundingClientRect();
        const pieceSize = boardRect.width / 8;
        const targetX = parseInt((e.clientX - boardRect.x) / pieceSize);
        const targetY = parseInt((e.clientY - boardRect.y) / pieceSize);

        setPieces(prevState => 
            prevState.map(p => 
                p.id === activePiece.id ? {...p, x: targetX, y: targetY} : p
            )
        );
        setActivePieceState(null);
    }

    function setActivePiece(e) {
        e.preventDefault();
        if (e.target.id === "boardImg") {
            activePiece && movePiece(e);
            return;
        }
        const clickedPiece = e.target;
        setActivePieceState(prev => 
            prev?.id === clickedPiece.id ? null : clickedPiece
        );
    }

    function dragPiece(e){
        if (!activePiece) return;

        const boardRect = boardRef.current.getBoundingClientRect();
        const pieceHalfSize = boardRect.width / 4;
        activePiece.style.left = `${e.clientX - boardRect.x - pieceHalfSize}px`;
        activePiece.style.top = `${e.clientY - boardRect.y - pieceHalfSize}px`;
    }

    useEffect(() => {
        setPieces(generateBoard());
    }, []);

    return(
        <div id="board" onMouseDown={setActivePiece} onMouseUp={movePiece} onMouseMove={dragPiece} >
            <img ref={boardRef} id="boardImg" src={boardImg} alt="board" draggable="false"/>
            {
                pieces.map(piece => <Piece key={piece.id} piece={piece}/>)
            }
        </div>
    )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:41:44