React 只重新渲染一个 setState 而不是另一个

React only re-rendering one setState but not the other

我正在用 React 创建一个简单的井字游戏。

目标: 每个按钮都有自己的状态。单击按钮后,它应将状态从“”切换为 'X',然后将播放器的全局状态更改为 'Player 2'。

问题: 它只是重新呈现 setPlayer() 调用而不是 setValue() 调用。但我摆脱了 setPlayer() 并只调用 setValue(),然后它按预期工作。我需要 setStates 才能工作并重新渲染,以便在玩家名称更改时显示 X。

这是一个 link 到实时沙盒版本 -
https://codesandbox.io/s/ticktacktoe-ozxjm?file=/src/App.js

import './App.css';
import { useState } from 'react';

function App() {

  const player1 = "Player 1"
  const player2 = "Player 2"
  const [player, setPlayer] = useState(player1)
 

  const WhosTurnMessage =()=>(
    <div className='message'>
      {player}, It's Your Turn
    </div>
  )


  const Buttons =()=>{
    const [value, setValue] = useState()

    const checkAndSet =()=>{
      if (player === player1){
        setValue('X')
        setPlayer(player2)
      }
      else {
        setValue('O')
        setPlayer(player1)
      }
    }

    return (
      <div className='grid-item'>
        <button className='btn' onClick={()=>checkAndSet()} > 
          {value} 
        </button>
      </div>
    )
  }

  const MapButtons =()=>(
    [1,2,3,4,5,6,7,8,9].map( i =>  <Buttons key={`button ${i}`}/>)
  )


  return (
    <div className="App">
      <header className="App-header">
        <h1>
          Tick Tack Toe 
        </h1>
      </header>

      <WhosTurnMessage />
      
      <main>
        <div className='grid-container'>

        <MapButtons />
          
        </div>
      </main>
    </div>
  );
}

export default App;

您的代码存在的问题是您在渲染过程中创建了全新类型的组件 App。这行不通。例如,每次 App 渲染时,都会创建一个新函数 Buttons。它可能与原始函数具有相同的文本,但所有 React 都知道它与上次不同。因此,由于它是一种不同类型的组件,因此 react 会卸载旧组件并安装新组件,从而清除所有状态。

您想要定义的任何组件都需要移到 App 之外,这样您只需创建一次,而不是在每次渲染时都创建它这肯定包括 Buttons,因为它有状态,虽然您可以将 WhosTurnMessageMapButtons 移出,但我认为它们不应该成为组成部分。我只想 App 计算 jsx 本身的那些位。

因此,如果您仍想将组件的某些部分分配给局部变量,我建议这样做:

function App() {
  const player1 = "Player 1"
  const player2 = "Player 2"
  const [player, setPlayer] = useState(player1)
 
  const whosTurnMessage = (
    <div className='message'>
      {player}, It's Your Turn
    </div>
  );

  const mapButtons = [1,2,3,4,5,6,7,8,9].map( i =>  <Buttons key={`button ${i}`}/>)

  return (
    <div className="App">
      <header className="App-header">
        <h1>
          Tick Tack Toe 
        </h1>
      </header>
        {whosTurnMessage}
      <main>
        <div className='grid-container'>
          {mapButtons}
        </div>
      </main>
    </div>
  )
}

const Buttons = () => {
  const [value, setValue] = useState();
  // ... same code you already have
}

或者就个人而言,我会在线执行 jsx,而不将其分配给变量:

function App() {
  const player1 = "Player 1"
  const player2 = "Player 2"
  const [player, setPlayer] = useState(player1)

  return (
    <div className="App">
      <header className="App-header">
        <h1>
          Tick Tack Toe 
        </h1>
      </header>
      <div className='message'>
        {player}, It's Your Turn
      </div>
      <main>
        <div className='grid-container'>
          {[1,2,3,4,5,6,7,8,9].map(i => <Buttons key={`button ${i}`}/>)}
        </div>
      </main>
    </div>
  )
}