Home > front end >  React only re-rendering one setState but not the other
React only re-rendering one setState but not the other

Time:02-06

I'm creating a simple Tic Tac Toe game in React.

The Goal: Each button has it's own state. After the button has been clicked it should switch the state from ' ' to an 'X' and then change the global state for player to say 'Player 2'.

The Problem: It's only re-rendering the setPlayer() call but not the setValue() call. But I'f I get rid of setPlayer() and only call setValue(), then it works as intended. I need both setStates to work and re-render so that an X is displayed the the player name changes.

Here is a link to a live sandbox version -
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;

CodePudding user response:

The problem with your code is that you're creating brand new types of components in the middle of rendering App. This won't work. For example, every time App renders, a new function Buttons is created. It may have the same text as the original function, but all react knows is that it's a different function from the last time. So since it's a different type of component, react unmounts the old one, and mounts the new one, wiping out all state.

Any component that you want to define needs to be moved outside of App, so that you just create it once, not on every render This definitely includes Buttons, since it has state, and while you could move WhosTurnMessage and MapButtons out, i don't think those deserve to be components. I would just have App calculate those bits of jsx itself.

So, i recommend this, if you still want to assign parts of the component to local variables:

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
}

Or personally, i would do the jsx in line, without assigning it to variables:

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>
  )
}
  •  Tags:  
  • Related