简体   繁体   English

选择性 state 未在 React 组件中更新

[英]Selective state not updating in React component

I'm trying to create a basic snake game in React.我正在尝试在 React 中创建一个基本的蛇游戏。 I seem to be misunderstanding state.我似乎误解了 state。 Right now the player can move, and the cherry gets placed for pickup, however when checking for collision inside of the move() function, I can't seem to update gameOver state.现在玩家可以移动,并且樱桃被放置以供拾取,但是当检查 move() function 内部的碰撞时,我似乎无法更新 gameOver state。 Is there something obvious I am missing?我有什么明显的遗漏吗? Appreciate you taking the time to read and respond.感谢您花时间阅读和回复。

import { useEffect, useState } from "react";

const Snake = () => {
  const timer = (ms) => new Promise((res) => setTimeout(res, ms));

  let [gameStarted, setGameStarted] = useState(false);
  let [gameOver, setGameOver] = useState(false);

  let [playerLocation, setPlayerLocation] = useState({ x: 20, y: 20 });
  let [cherryLocation, setCherryLocation] = useState({});

  let direction = "W";

  const tableWidth = 40;
  const tableHeight = 40;
  const cellSize = "10px";

  const handleKeyPress = (e) => {
    switch (e.code) {
      case "ArrowLeft":
        direction = "W";
        break;
      case "ArrowRight":
        direction = "E";
        break;
      case "ArrowUp":
        direction = "N";
        break;
      case "ArrowDown":
        direction = "S";
        break;
    }
  };

  function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min) + min);
  }

  const placeCherry = () => {
    return {
      x: getRandomInt(0, tableWidth),
      y: getRandomInt(0, tableHeight)
    };
  };

  const checkCollision = () => {
    if (playerLocation.x <= 0 || playerLocation.x >= tableWidth || playerLocation.y <= 0 || playerLocation.y >= tableHeight) {
      return true
    }
    return false
  };

  const startGame = () => {
    if (!gameStarted) {
      setGameStarted(!gameStarted);
      setGameOver(!gameOver);
      update();
    }
  };

  const move = () => {
    let newX = playerLocation.x;
    let newY = playerLocation.y;
    switch (direction) {
      case "N":
        newY = playerLocation.y -= 1;
        break;
      case "S":
        newY = playerLocation.y += 1;
        break;
      case "E":
        newX = playerLocation.x += 1;
        break;
      case "W":
        newX = playerLocation.x -= 1;
        break;
    }
    if(checkCollision()) {
      setGameOver(!gameOver);
    }
    setPlayerLocation((prevState) => ({
      ...prevState,
      x: newX,
      y: newY
    }));
  };

  const update = async () => {
    setCherryLocation(placeCherry());
    while (!gameOver) {
      console.log(gameOver);
      await timer(100);
      move();
    }
    alert("Game Over!");
  };

  
  const renderBoard = () => {
    return (
      <table style={{ border: "2px solid rgba(0, 0, 0, 0.05)" }}>
        <tbody>
          {[...Array(tableHeight)].map((y, i) => {
            return (
              <tr key={i}>
                {[...Array(tableWidth)].map((x, j) => {
                  if (playerLocation.y === i && playerLocation.x === j) {
                    return (
                      <td
                        style={{
                          width: cellSize,
                          height: cellSize,
                          backgroundColor: "green"
                        }}
                        key={j}
                      >
                        {" "}
                      </td>
                    );
                  }
                  if (cherryLocation.y === i && cherryLocation.x === j) {
                    return (
                      <td
                        style={{
                          width: cellSize,
                          height: cellSize,
                          backgroundColor: "pink"
                        }}
                        key={j}
                      >
                        {" "}
                      </td>
                    );
                  }
                  return (
                    <td
                      style={{
                        width: cellSize,
                        height: cellSize,
                        backgroundColor: "black"
                      }}
                      key={j}
                    >
                      {" "}
                    </td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
    );
  };

  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);

    return () => {
      window.removeEventListener("keydown", handleKeyPress);
    };
  }, []);

  return (
    <div>
      <p>Snake</p>
      <div style={{ display: "flex", justifyContent: "center" }}>
        {renderBoard()}
      </div>
      <div>
        <button
          disabled={gameOver}
          style={{ width: "250px", height: "50px", marginTop: "30px" }}
          onClick={startGame}
        >
          Start
        </button>
      </div>
    </div>
  );
};

export default Snake;

Yes, the state update seems too convoluted there.是的,state 更新似乎太复杂了。 Here, setPlayerLocation() is supposed to update only the playerLocation variable.在这里, setPlayerLocation()应该只更新playerLocation变量。 So, you can just say,所以,你只能说,

setPlayerLocation({
  x : theNewX,
  y : theNewY
});

And it should update the playerLocation state.它应该更新playerLocation state。

The gameOver state was not being updated inside the async function,which leads to an infinite while loop. gameOver state 没有在异步 function 内更新,这会导致无限循环。 So I tried to replace the timer with setInterval updater function.所以我尝试用 setInterval updater function 替换定时器。

Replace the existing functions with following to proceed.用以下替换现有功能以继续。

 const timerIdRef = useRef();
    
    useEffect(() => {
      if (gameOver) {
        alert("Game Over!");
      }
    
      if (!gameOver && !checkCollision()) {
        setCherryLocation(placeCherry());
      }
    
      return () => clearInterval(timerIdRef.current);
    }, [gameOver]);
    
    const handleKeyPress = (e) => {
      switch (e.code) {
        case "ArrowLeft":
          direction = "W";
          update();
          break;
        case "ArrowRight":
          direction = "E";
          update();
          break;
        case "ArrowUp":
          direction = "N";
          update();
          break;
        case "ArrowDown":
          direction = "S";
          update();
          break;
      }
    };
    
    const startGame = () => {
      if (!gameStarted) {
        setGameStarted(true);
        setGameOver(false);
        update();
      }
    };
    
    const move = () => {
      let newX = playerLocation.x;
      let newY = playerLocation.y;
      switch (direction) {
        case "N":
          newY = playerLocation.y -= 1;
          break;
        case "S":
          newY = playerLocation.y += 1;
          break;
        case "E":
          newX = playerLocation.x += 1;
          break;
        case "W":
          newX = playerLocation.x -= 1;
          break;
      }
    
      if (checkCollision()) {
        setGameOver(true);
      }
      setPlayerLocation({
        x: newX,
        y: newY
      });
    };
    
    const update = () => {
      clearInterval(timerIdRef.current);
      if (!gameOver) {
        timerIdRef.current = setInterval(() => move(), 100);
      }
    };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM