You need to enable JavaScript to run this app. React战舰游戏项目中如何修改数组指定位置元素的classList更改样式
优惠活动
大模型
产品
解决方案
定价
更多

React战舰游戏项目中如何修改数组指定位置元素的classList更改样式

问题根源
  • 你的boxes数组存储的是0、1的数值,不是DOM元素,不存在classList属性,直接调用该属性修改类名必然无效
  • React遵循数据驱动视图的渲染逻辑,你手动修改DOM的操作会在组件重渲染时被还原,不符合React的开发范式
  • 判断条件if (boxes === 0)永远不会成立,boxes是二维数组,不可能等于数字0,你的修改逻辑从一开始就没有执行
解决方法

改用状态管理网格数据和击中状态,通过修改状态来驱动类名自动更新,不需要手动操作DOM:

  1. 把网格数据和回合状态都存入useState,避免组件重渲染时数据被重置
  2. 新增二维数组状态存储每个格子的击中状态,AI射击时只需要修改这个状态的值
  3. 把AI射击逻辑放在useEffect中,监听回合状态触发射击操作
  4. 渲染格子的时候直接根据击中状态动态绑定className

修改后的完整代码

import React, { useEffect, useState } from "react";
import Ship from "../components/ShipGenerate.js";
import "../style/style.css";

function Grid(props) {
  // 初始化10*10的网格,1代表有船,0代表空
  const initGrid = () => {
    const grid = new Array(10).fill(0).map(() => new Array(10).fill(0));
    const rnd = (r) => Math.trunc(Math.random() * r);
    for (let l = 0; l < 40; l += 1) {
      grid[rnd(10)][rnd(10)] = 1;
    }
    return grid;
  };

  // 初始化击中状态,0=未击中,1=击中船(绿),2=击中空(红)
  const initHitState = () => new Array(10).fill(0).map(() => new Array(10).fill(0));

  const [grid, setGrid] = useState(initGrid);
  const [hitState, setHitState] = useState(initHitState);
  const [turn, setTurn] = useState(true); // true=玩家回合,false=AI回合

  // AI射击逻辑,监听回合状态,AI回合时触发
  useEffect(() => {
    if (!turn) {
      let x, y;
      // 选一个没被击中过的位置
      do {
        x = Math.floor(Math.random() * 10);
        y = Math.floor(Math.random() * 10);
      } while (hitState[x][y] !== 0);

      // 更新击中状态
      const newHitState = hitState.map(row => [...row]);
      newHitState[x][y] = grid[x][y] === 1 ? 1 : 2;
      setHitState(newHitState);
      // 射击完切换回玩家回合
      setTurn(true);
    }
  }, [turn, hitState, grid]);

  const handleClick = (e, x, y) => {
    // 只有玩家回合才能点击
    if (!turn || hitState[x][y] !== 0) return;
    // 更新玩家击中状态
    const newHitState = hitState.map(row => [...row]);
    newHitState[x][y] = grid[x][y] === 1 ? 1 : 2;
    setHitState(newHitState);
    // 切换到AI回合
    setTurn(false);
  };

  return (
    <div className="game-wrapper">
      <div className="game-area">
        {grid.map((row, x) => {
          return row.map((box, y) => {
            // 动态绑定类名
            let className = "ship";
            if (hitState[x][y] === 1) className = "green";
            if (hitState[x][y] === 2) className = "red";
            return (
              <div
                key={`${x}-${y}`}
                onClick={(e) => handleClick(e, x, y)}
                className={className}
              >
              </div>
            );
          });
        })}
      </div>
    </div>
  );
}

export default Grid;
补充说明
  • 所有网格相关的数据都存在状态里,修改状态后React会自动更新对应DOM的类名,不需要你手动操作classList
  • 加了重复射击的判断,避免AI反复打同一个位置
  • 你原来写的myShips查询DOM的逻辑完全可以删掉,React项目里除了特殊场景不要直接操作DOM

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 03:51:03