【问题标题】:React only re-rendering one setState but not the otherReact 只重新渲染一个 setState 而不是另一个
【发布时间】:2022-02-06 08:51:58
【问题描述】:

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

目标: 每个按钮都有自己的状态。单击按钮后,它应该将状态从“”切换到“X”,然后将玩家的全局状态更改为“玩家 2”。

问题: 它只是重新渲染 setPlayer() 调用,而不是 setValue() 调用。但是如果我摆脱 setPlayer() 并且只调用 setValue(),那么它会按预期工作。我需要 setStates 工作并重新渲染,以便在玩家名称更改时显示 X。

这是一个实时沙盒版本的链接 -
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;

【问题讨论】:

  • 这是由于组件的父子关系。把所有组件分开,你的代码架构不好。

标签: javascript reactjs asynchronous setstate tic-tac-toe


【解决方案1】:

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

您想要定义的任何组件都需要移到App 之外,这样您只需创建一次,而不是在每次渲染时这肯定包括Buttons,因为它有状态,而您可以移动@ 987654326@ 和 MapButtons 出来,我认为这些不应该成为组件。我只想让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>
  )
}

【讨论】:

  • 在您链接的页面中,Buttons 仍在 App 中。你需要把它移出去。另外,我忽略了 Buttons 使用 App 中的东西,因此您需要将这些值作为道具传递给 Buttons。
  • 搞定了。花了一段时间才把我的头绕过去。谢谢尼古拉斯。
猜你喜欢
  • 2019-01-05
  • 2019-09-26
  • 2018-05-27
  • 2020-06-28
  • 2021-04-28
  • 2018-04-04
  • 2016-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多