在事件处理程序内重新渲染

huangapple go评论58阅读模式
英文:

Re-rendering inside an event handler

问题


import { useState } from "react";
function Square({ value, onSquareClick, winning }) {
  console.log(winning);
  return (
    <button
      className={winning ? "newsquare" : "square"}
      onClick={onSquareClick}
    >
      {value}
    </button>
  );
}

function Board({ xIsNext, squares, onPlay, winning, handleWinning }) {
  let winner;
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    onPlay(nextSquares);
    winner = calculateWinner(nextSquares);
    console.log(squares + " "); // &lt;----------------------------------here
    if (winner) handleWinning(winner);
  }
  console.log(squares + " ");   // &lt;----------------------------------here
  let status;
  if (winner) {
    status = "Winner: " + squares[winner[0]];
    console.log(winner);
  } else {
    status = "Next player: " + (xIsNext ? "X" : "O");
  }

  return (
    <>
      <div className="status">{status}</div>
      <div className="board-row">
        <Square
          value={squares[0]}
          onSquareClick={() => handleClick(0)}
          winning={winning[0]}
        />
        <Square
          value={squares[1]}
          onSquareClick={() => handleClick(1)}
          winning={winning[1]}
        />
        <Square
          value={squares[2]}
          onSquareClick={() => handleClick(2)}
          winning={winning[2]}
        />
      </div>
      <div className="board-row">
        <Square
          value={squares[3]}
          onSquareClick={() => handleClick(3)}
          winning={winning[3]}
        />
        <Square
          value={squares[4]}
          onSquareClick={() => handleClick(4)}
          winning={winning[4]}
        />
        <Square
          value={squares[5]}
          onSquareClick={() => handleClick(5)}
          winning={winning[5]}
        />
      </div>
      <div className="board-row">
        <Square
          value={squares[6]}
          onSquareClick={() => handleClick(6)}
          winning={winning[6]}
        />
        <Square
          value={squares[7]}
          onSquareClick={() => handleClick(7)}
          winning={winning[7]}
        />
        <Square
          value={squares[8]}
          onSquareClick={() => handleClick(8)}
          winning={winning[8]}
        />
      </div>
    </>
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const [winning, setWinning] = useState(Array(9).fill(false));
  const xIsNext = currentMove % 2 === 0;
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }
  function handleWinning(winningSquare) {
    const newWinning = winning.slice();
    newWinning[winningSquare[0]] = true;
    newWinning[winningSquare[1]] = true;
    newWinning[winningSquare[2]] = true;
    setWinning(newWinning);
  }
  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = "Go to move #" + move;
    } else {
      description = "Go to game start";
    }
    return (
      <li key={move}>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board
          xIsNext={xIsNext}
          squares={currentSquares}
          onPlay={handlePlay}
          winning={winning}
          handleWinning={handleWinning}
        />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </div>
  );
}

function calculateWinner(squares) {
  const lines = [
    [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 (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      const winningSquares = lines[i].slice();
      console.log(winningSquares);
      return winningSquares;
    }
  }
  return null;
}

我目前正在开发一个使用React.js的井字棋游戏应用程序,我在处理状态更新时遇到了一个控制台日志输出的问题。在阅读有关React状态更新和重新渲染行为的文章后,我预期两个console.log(squares)语句应该显示相同的输出。

我想了解这种行为背后的原因,以及如何确保两个console.log(squares)语句显示相同的输出。

任何见解或建议将不胜感激!谢谢!

英文:

import { useState } from &quot;react&quot;;
function Square({ value, onSquareClick, winning }) {
  console.log(winning);
  return (
    &lt;button
      className={winning ? &quot;newsquare&quot; : &quot;square&quot;}
      onClick={onSquareClick}
    &gt;
      {value}
    &lt;/button&gt;
  );
}

function Board({ xIsNext, squares, onPlay, winning, handleWinning }) {
  let winner;
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = &quot;X&quot;;
    } else {
      nextSquares[i] = &quot;O&quot;;
    }
    onPlay(nextSquares);
    winner = calculateWinner(nextSquares);
    console.log(squares + &quot; &quot;); // &lt;----------------------------------here
    if (winner) handleWinning(winner);
  }
  console.log(squares + &quot; &quot;);   // &lt;----------------------------------here
  let status;
  if (winner) {
    status = &quot;Winner: &quot; + squares[winner[0]];
    console.log(winner);
  } else {
    status = &quot;Next player: &quot; + (xIsNext ? &quot;X&quot; : &quot;O&quot;);
  }

  return (
    &lt;&gt;
      &lt;div className=&quot;status&quot;&gt;{status}&lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square
          value={squares[0]}
          onSquareClick={() =&gt; handleClick(0)}
          winning={winning[0]}
        /&gt;
        &lt;Square
          value={squares[1]}
          onSquareClick={() =&gt; handleClick(1)}
          winning={winning[1]}
        /&gt;
        &lt;Square
          value={squares[2]}
          onSquareClick={() =&gt; handleClick(2)}
          winning={winning[2]}
        /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square
          value={squares[3]}
          onSquareClick={() =&gt; handleClick(3)}
          winning={winning[3]}
        /&gt;
        &lt;Square
          value={squares[4]}
          onSquareClick={() =&gt; handleClick(4)}
          winning={winning[4]}
        /&gt;
        &lt;Square
          value={squares[5]}
          onSquareClick={() =&gt; handleClick(5)}
          winning={winning[5]}
        /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square
          value={squares[6]}
          onSquareClick={() =&gt; handleClick(6)}
          winning={winning[6]}
        /&gt;
        &lt;Square
          value={squares[7]}
          onSquareClick={() =&gt; handleClick(7)}
          winning={winning[7]}
        /&gt;
        &lt;Square
          value={squares[8]}
          onSquareClick={() =&gt; handleClick(8)}
          winning={winning[8]}
        /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const [winning, setWinning] = useState(Array(9).fill(false));
  const xIsNext = currentMove % 2 === 0;
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }
  function handleWinning(winningSquare) {
    const newWinning = winning.slice();
    newWinning[winningSquare[0]] = true;
    newWinning[winningSquare[1]] = true;
    newWinning[winningSquare[2]] = true;
    setWinning(newWinning);
  }
  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  const moves = history.map((squares, move) =&gt; {
    let description;
    if (move &gt; 0) {
      description = &quot;Go to move #&quot; + move;
    } else {
      description = &quot;Go to game start&quot;;
    }
    return (
      &lt;li key={move}&gt;
        &lt;button onClick={() =&gt; jumpTo(move)}&gt;{description}&lt;/button&gt;
      &lt;/li&gt;
    );
  });

  return (
    &lt;div className=&quot;game&quot;&gt;
      &lt;div className=&quot;game-board&quot;&gt;
        &lt;Board
          xIsNext={xIsNext}
          squares={currentSquares}
          onPlay={handlePlay}
          winning={winning}
          handleWinning={handleWinning}
        /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;game-info&quot;&gt;
        &lt;ol&gt;{moves}&lt;/ol&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

function calculateWinner(squares) {
  const lines = [
    [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 (let i = 0; i &lt; lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] &amp;&amp; squares[a] === squares[b] &amp;&amp; squares[a] === squares[c]) {
      const winningSquares = lines[i].slice();
      console.log(winningSquares);
      return winningSquares;
    }
  }
  return null;
}

I am currently working on a React.js application for a tic-tac-toe game, and I've encountered an issue with the console log output while handling state updates. After reading about React's state updates and re-rendering behavior, I expected both console.log(squares) statements to show the same output.

I would like to understand the reason behind this behavior and how I can ensure that both console.log(squares) statements show the same output.

Any insights or suggestions would be greatly appreciated! Thank you!

答案1

得分: 1

React更新以及状态变化在React中都是异步的。这意味着当你调用onPlay时,"squares"状态被上传,但是handleClick此时并不等待看状态是否实际被设置。所以你在更新之前就得到了输出。

如果你主要关心的是在状态更新时得到console.log,你可以使用useEffect钩子。

React.useEffect(() => {
  console.log(squares);
}, [squares]);

你也可以将这个钩子用作一种回调,一旦状态发生变化就会触发。

参考:https://react.dev/reference/react/useEffect

英文:

React updates as well as the state changes are asynchronous with react. This means that when you call onPlay, the "squares" state is uploaded, but handleClick does not wait at this point to see if the state has actually been set. So you get the output before the update.

If your main concern is to get a console.log on status updates, you could use the useEffect hook

React.useEffect(() =&gt; {
  console.log(squares);
}, [squares]);

you can also use this hook as a kind of callback as soon as a state has been changed

Ref: https://react.dev/reference/react/useEffect

huangapple
  • 本文由 发表于 2023年7月27日 15:16:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76777308.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定