• Empleos
  • Sobre nosotros
  • profesionales
    • Inicio
    • Empleos
    • Cursos y retos
  • empresas
    • Inicio
    • Publicar vacante
    • Nuestro proceso
    • Precios
    • Evaluaciones
    • Nómina
    • Blog
    • Comercial
    • Calculadora de salario

0

101
Vistas
Access and change the state array element in main React App from grandchild

I apologize for the headline of this question, but I tried to explain the problem as much as I could in one line.

Our school assignment required us to create a (sort of) Tic Tac Toe game but in a certain way. Instead of creating nine different Box elements from the main App, we were required to create Row class and Box class, so the main App will have three Row class children and each Row class will have three Box children.

Now the problem I encountered is how to change the main App state array at a particular element that corresponds to that exact Box component.

I am a beginner and I apologize in advance if I am not seeing something too obvious. If you have any questions, please do ask, I'll be more than happy to explain this as much as I can. Thank you!

    import React, { Component } from 'react';
    import { render } from 'react-dom';
    
    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          0 : ['-','-','-'],
          1 : ['-','-','-'],
          2 : ['-','-','-']
        };
        this.handleClick = this.handleClick.bind(this)
      }
      handleClick(){
        // if that particular element is '-' change from '-' to 'X'
        // else if it is 'X' change to 'O'
        // else change to 'X'
      }
      render() {
    
        const rows = []
        for (let i = 0; i < 3; i++) {
          rows.push(<Row key = {i} msg = {this.state[i]} handleClick = {this.handleClick}/>)
        }
    
        return (
          <div>
            <div>
              <h1>Tic Tac Toe</h1>
            </div>
            <div id = 'main'>
              {rows}
            </div>
          </div>
        );
      }
    }
    class Row extends Component{
    
      render() {
        const boxes = []
        for (let i = 0; i < 3; i++) {
          boxes.push(<Box key = {i} msg = {this.props.msg[i]} handleClick = {this.props.handleClick}/>)
        }
        return (
          <div className = 'row'>
            {boxes}
          </div>
        )
      }
    
    }
    
    class Box extends Component{
    
      render() {
        return (
          <div className = 'box' onClick = {this.props.handleClick}>
            <p>{this.props.msg}</p>
          </div>
        )
      }
    
    }
    render(<App />, document.querySelector('#root'));
about 3 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

You have organized your components well. You need to handle player switching after each click and updating the values in the game board.

  1. Add another state variable called player (it can just be a boolean as this is a two-player game)
this.state = {
      0: ["-", "-", "-"],
      1: ["-", "-", "-"],
      2: ["-", "-", "-"],
      player: true
    };
  1. You can pass row and col index to handleClick function like below.
  handleClick(row, col) {
    // set the corresponding value (using row and colum index) in the game borad
    this.setState(
      {
        [row]: this.state[row].map((val, colId) =>
          colId === col ? (this.state.player ? "X" : "O") : val
        )
      },
      () => {
        // switch the player (toggle the player boolean value)
        this.setState({ player: !this.state.player });
      }
    );
  }
  1. Pass the row and col ids from the click handler in Box component.
       <Box
          ...
          ...
          handleClick={() => {
            this.props.handleClick(this.props.row, i);
          }}
        />

Code Sandbox

NOTE: when you develop further to avoid changing the same Box by clicking, you can keep an object to store more info like { value: "-", used: false } instead of "-" as values.

about 3 years ago · Juan Pablo Isaza Denunciar

0

Building off of @Amila Senadheera's answer, you could also add a check in the handleClick function to ensure that a player can't overwrite another player's move. Something along the lines of:

handleClick(row, col) {
    // set the corresponding value (using row and colum index) in the game board
    if(this.state[row][col] !== '-') {
      return // Don't allow the player to overwrite an already-marked square.
    }
    this.setState(
      {
        [row]: this.state[row].map((val, colId) =>
          colId === col ? (this.state.player ? "X" : "O") : val
        )
      },
      () => {
        // switch the player (toggle the player boolean value)
        this.setState({ player: !this.state.player });
      }
    );
  }

And if you want to add a check for victory, there's some useful discussion here: https://codereview.stackexchange.com/questions/24764/tic-tac-toe-victory-check.

about 3 years ago · Juan Pablo Isaza Denunciar

0

You could define parameters for handleClick like

type T = 1 | 2 | 3;
function handleClick(row: T, col: T) {
  const newRow = [ ...this.state[row] ];
  this.sign = this.sign === "O" ? "X" : "O";
  newRow[col] = this.sign;
  this.setState({...this.state, [row]: newRow});
}

and then you need new props for Box:

for (let i = 0; i < 3; i++) {
  boxes.push(
    <Box 
      row={props.row}
      col={i}
      key={i} 
      msg={this.props.msg[i]} 
      handleClick={this.props.handleClick}/>
  )
}
about 3 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Nuestro proceso Comercial
Legal
Términos y condiciones Política de privacidad
© 2025 PeakU Inc. All Rights Reserved.

Andres GPT

Recomiéndame algunas ofertas
Necesito ayuda