I am trying to adapt the tic-tac-toe React tutorial and create a simple bingo game (with the ability to change the board size). I have the following component hierarchy:
App -> Game -> Board -> Square
The OnClick function gets passed down from the Game to the Square via props. Once I have calculated that the user has Bingo, I pop up a Dialog with a button allowing them to refresh the game (render new items in each square and reset the "hasBingo" state). For this I am using a consistent function (refreshBoard) which also runs when the board is first rendered and when the rows prop is changes. When I click the button on the dialog, the board refreshes and looks ready for a new round, however the onClick event on the squares does not get fired anymore (it appears that the prop is still there in dev tools though).
If I change the rows prop by setting the state in App, the board re-renders and works fine. Is there something I am missing with the dialog? Could it still be rendered on top of the board and "hijacking" the clicks, even though it's not visible?
Stack Blitz here: https://react-jdfqfo.stackblitz.io
class Game extends React.Component {
constructor(props) {
super(props);
let numSquares = this.props.rows * this.props.rows;
this.state = ( {
numSquares : numSquares,
squares : Array(numSquares).fill(false),
phrases: Array(numSquares).fill(false),
hasBingo: false,
rows: this.props.rows,
});
}
refreshBoard(rows) {
//Reset the board
let numSquares = rows * rows;
this.setState({hasBingo:false,
rows: rows,
squares: Array(rows * rows).fill(false),
phrases: Array(rows * rows).fill(false) })
//Get new items from API to show on the board
fetch('http://xxx')
.then(res => res.json())
.then((data) => {
this.setState({ phrases: data.map(( {text}) => text) })
})
}
componentDidMount() {
this.refreshBoard(this.state.rows);
}
componentWillReceiveProps(nextProps) {
//The App component state could change the number of rows, listen for that event
this.setState({ rows: nextProps.rows });
this.refreshBoard(nextProps.rows);
}
handleClick(i) {
const squares = this.state.squares.slice();
if (this.state.hasBingo) {
return;
}
squares[i] = !squares[i];
this.setState({
squares: squares,
});
}
calculateBingo(squares, rows) {
//Returns true for bingo, or null. Code removed for brevity
return null;
}
render() {
let squares = this.state.squares;
let winner = this.calculateBingo(squares, this.state.rows);
let phrases = this.state.phrases;
let Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
let status;
if (winner) {
this.state.hasBingo = true;
}
return (
<div className="game">
<div className="game-board">
<Board
squares={squares}
phrases={phrases}
rows={this.state.rows}
onClick={(i) => this.handleClick(i)}/>
</div>
<div className="game-info">
<Dialog
open={this.state.hasBingo}
TransitionComponent={Transition}
keepMounted
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle id="alert-dialog-slide-title">{"BINGO"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-slide-description">
Well done!
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.refreshBoard(this.state.rows)} color="primary">
Start New Game
</Button>
</DialogActions>
</Dialog>
</div>
</div>
);
}
}
export default Game;