Access and change the state array element in main React App from grandchild
Solution 1:
You have organized your components well. You need to handle player switching after each click and updating the values in the game board.
- 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
};
- 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 });
}
);
}
- Pass the
row
andcol
ids from the click handler inBox
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.
Solution 2:
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}/>
)
}
Solution 3:
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.