Me disculpo por el título de esta pregunta, pero traté de explicar el problema tanto como pude en una sola línea.
Nuestra tarea escolar requería que creáramos un (tipo de) juego de Tic Tac Toe pero de cierta manera. En lugar de crear nueve elementos Box diferentes a partir de la App
principal, se nos solicitó crear una clase Row
y una clase Box
, por lo que la aplicación principal tendrá tres hijos de clase Row
y cada clase Row
tendrá tres hijos Box
.
Ahora, el problema que encontré es cómo cambiar la matriz de estado de la aplicación principal en un elemento particular que corresponde a ese componente Box exacto.
Soy un principiante y pido disculpas de antemano si no veo algo demasiado obvio. Si tiene alguna pregunta, por favor pregunte, estaré más que feliz de explicar esto tanto como pueda. ¡Gracias!
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'));
Has organizado bien tus componentes. Debe manejar el cambio de jugador después de cada clic y actualizar los valores en el tablero de juego.
player
(puede ser simplemente un booleano ya que este es un juego de dos jugadores) this.state = { 0: ["-", "-", "-"], 1: ["-", "-", "-"], 2: ["-", "-", "-"], player: true };
handleClick
como se muestra a continuación. 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 }); } ); }
row
y los col
del controlador de clics en el componente Box
. <Box ... ... handleClick={() => { this.props.handleClick(this.props.row, i); }} />
NOTA: cuando desarrolle más para evitar cambiar el mismo cuadro haciendo clic, puede mantener un objeto para almacenar más información como { value: "-", used: false }
en lugar de "-"
como valores.
Partiendo de la respuesta de @Amila Senadheera, también podría agregar una verificación en la función handleClick para asegurarse de que un jugador no pueda sobrescribir el movimiento de otro jugador. Algo del estilo de:
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 }); } ); }
Y si desea agregar un cheque por la victoria, hay una discusión útil aquí: https://codereview.stackexchange.com/questions/24764/tic-tac-toe-victory-check .
Podrías definir parámetros para handleClick como
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}); }
y luego necesitas nuevos accesorios para 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}/> ) }