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

React井字棋开发问题:如何仅为获胜棋子添加颜色?

React井字棋问题:仅高亮获胜棋子而非全部同色棋子

我用React开发的井字棋游戏功能正常,能正确识别胜者,但存在一个问题:当玩家X获胜时,所有X棋子都会被染成绿色,而非仅将获胜的三个X标绿。我希望只给获胜的棋子添加绿色样式,试过用状态存储颜色但会导致所有棋子变色,也知晓不能直接操作DOM。以下是主组件Grid和Box组件的代码:

主组件Grid代码

export default function Grid() {
    // Game state
    const [grid, setGrid] = useState(new Array(9).fill(""));
    const [turn, setTurn] = useState("P1") //toggle turns for P1 and P2
    const [color, setColor] = useState("black")
    
    // Players state
    const [countWinP1, setCountWinP1] = useState(0)
    const [countWinP2, setCountWinP2] = useState(0)

    const handleClick = (event) => {
        const clickValue = event.target.value
        
        setGrid(grid.map((btn, index) => {
            if (index+1 == clickValue && turn === "P1") {
                return btn = "X"
            } else if (index+1 == clickValue && turn === "P2") {
                return btn = "0"
            }
            return btn
        }))
        handleToggle();
    }
    

    // WIN COUNT
    useEffect(() => {
        const newCountP1 = countWinP1 + 1;
        const newCountP2 = countWinP2 + 1;
        if (handleWinner(grid) && turn === "P2") {
            setCountWinP1(newCountP1); 
        } else
        if (handleWinner(grid) && turn === "P1") {
            setCountWinP2(newCountP2); 
        }
    }, [grid]);

    // RESETS
    const handleToggle = () => {
        if (turn === "P1") {
            setTurn("P2")
        } else {
            setTurn("P1")
        }         
    }
    const handleReset = () => {
        setGrid(new Array(9).fill(0));
        setTurn("P1")
    }
    const handlePlayersReset = () => {
        setCountWinP1(0)
        setCountWinP2(0)
    }


    return (
        <>
        <WinCount countWinP1={countWinP1} countWinP2={countWinP2} PlayersReset={handlePlayersReset}/>
        <Box grid={grid} handleClick={handleClick} handleWinner={handleWinner(grid)} handleReset={handleReset} turn={turn} color={color}/>     
    </>
  )
}

Box组件代码

export default function Box({grid, handleClick, handleWinner, handleReset, turn, color}) {

  return (
    <>
     <div className='container'>
        <h1>
            {handleWinner && turn==="P2" ? "WINNER: PLAYER 1" : handleWinner && turn==="P1" ? "WINNER: PLAYER 2" : "GAME ON"}
        </h1>

     
        <div className='box'>
           {grid.map((btn, index) => {
                if (btn === "X") {
                    return <button id={index+1} key={uuid()} value={index+1} onClick={handleClick} disabled={true}><strong style={{fontSize:30, color: handleWinner && turn=== "P2"? "green" : handleWinner && turn=== "P1" ? "red" : "black" }}>X</strong></button>
                        } else 
                if (btn === "0") {
                    return <button id={index+1} key={uuid()} value={index+1} onClick={handleClick} disabled={true}><strong style={{fontSize:30, color: handleWinner && turn=== "P1"? "green" : handleWinner && turn=== "P2" ? "red" : "black" }}>0</strong></button>
                        } else  
                    return <button id={index+1} key={uuid()} value={index+1} onClick={handleClick} disabled={handleWinner? true : false}>Click me</button>
                    })}
        </div>

        <button className="grid_reset" onClick={handleReset}>RESET</button>
    </div>
    </>
  )
}

解决方案

问题核心在于你当前的handleWinner函数只返回布尔值,无法区分哪些棋子是获胜序列的一部分。我们需要修改它返回获胜棋子的索引数组,再基于这个数组判断每个棋子是否需要高亮。

步骤1:修改handleWinner函数,返回获胜索引数组

补充或修改handleWinner函数,让它返回获胜的三个棋子索引,没有胜者则返回null:

const handleWinner = (grid) => {
  // 所有可能的获胜组合(对应网格的索引)
  const winPatterns = [
    [0,1,2], [3,4,5], [6,7,8], // 横向
    [0,3,6], [1,4,7], [2,5,8], // 纵向
    [0,4,8], [2,4,6] // 对角线
  ];
  // 遍历所有获胜组合
  for (const pattern of winPatterns) {
    const [a, b, c] = pattern;
    // 判断三个位置的棋子是否相同且不为空
    if (grid[a] && grid[a] === grid[b] && grid[a] === grid[c]) {
      return pattern; // 返回获胜的索引数组
    }
  }
  return null; // 无胜者
};

步骤2:在Grid组件中存储获胜索引状态

添加状态存储获胜棋子的索引数组,并更新useEffect的逻辑:

// 在Grid组件的状态中添加
const [winningIndices, setWinningIndices] = useState(null);

// 修改原有的useEffect
useEffect(() => {
  const winningCells = handleWinner(grid);
  if (winningCells) {
    setWinningIndices(winningCells);
    // 用函数式更新计数,避免依赖问题
    if (turn === "P2") {
      setCountWinP1(prev => prev + 1);
    } else {
      setCountWinP2(prev => prev + 1);
    }
  } else {
    setWinningIndices(null);
  }
}, [grid, turn]);

// 修改Box组件的props,传入winningIndices而非handleWinner布尔值
<Box 
  grid={grid} 
  handleClick={handleClick} 
  winningIndices={winningIndices} 
  handleReset={handleReset} 
  turn={turn} 
/>

步骤3:修改Box组件,根据获胜索引判断颜色

更新Box组件的渲染逻辑,仅给获胜索引内的棋子标绿:

export default function Box({grid, handleClick, winningIndices, handleReset, turn}) {
  // 判断是否有胜者
  const hasWinner = !!winningIndices;

  return (
    <>
     <div className='container'>
        <h1>
            {hasWinner && turn==="P2" ? "WINNER: PLAYER 1" : hasWinner && turn==="P1" ? "WINNER: PLAYER 2" : "GAME ON"}
        </h1>

     
        <div className='box'>
           {grid.map((btn, index) => {
                let textColor = "black";
                // 如果当前棋子在获胜索引数组中,标绿
                if (winningIndices && winningIndices.includes(index)) {
                  textColor = "green";
                } 
                // 如果有胜者但当前棋子不在获胜序列中,标红(可根据需求调整)
                else if (hasWinner) {
                  textColor = "red";
                }

                if (btn === "X") {
                    return (
                      <button id={index+1} key={uuid()} value={index+1} onClick={handleClick} disabled={true}>
                        <strong style={{fontSize:30, color: textColor}}>X</strong>
                      </button>
                    );
                } else if (btn === "0") {
                    return (
                      <button id={index+1} key={uuid()} value={index+1} onClick={handleClick} disabled={true}>
                        <strong style={{fontSize:30, color: textColor}}>0</strong>
                      </button>
                    );
                } else {
                    return (
                      <button id={index+1} key={uuid()} value={index+1} onClick={handleClick} disabled={hasWinner}>
                        Click me
                      </button>
                    );
                }
           })}
        </div>

        <button className="grid_reset" onClick={handleReset}>RESET</button>
    </div>
    </>
  )
}

步骤4:修复其他小问题

  • handleReset中setGrid(new Array(9).fill(0))改为setGrid(new Array(9).fill("")),和初始状态保持一致
  • 移除无用的color状态,因为现在不需要全局控制颜色

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:24:57