2
\$\begingroup\$

I wrote this tic tac toe game in react. I am learning react now. Feedback is welcome. I deliberately used single file, to focus on React and not other aspects.

<!-- 3x3 Tic tac toe implementation in React JS. -->
<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8"/>
 <title>Tic Tac Toe</title>
 <!-- Import react -->
 <script src="https://unpkg.com/react@latest/dist/react.js"></script>
 <script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"></script>
 <!-- Import Babel -->
 <script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
 /*
 * 3x3 Tic tac toe game component.
 *
 * */
 class TicTacToe extends React.Component {
 // Constructor for our component.
 constructor(props) {
 // Call parent constructor
 super(props);
 // Initially board is in uninitialized state.
 this.state = {
 board: [null, null, null, null, null, null, null, null, null]
 };
 // Bind handlers
 this.handleClick = this.handleClick.bind(this);
 this.restart = this.restart.bind(this);
 // Whose turn is it?
 this.turn = 0;
 // Indicates if game is finished or not.
 this.gameFinished = false;
 }
 // Handler when user clicks an image on the board.
 handleClick(id) {
 if (this.gameFinished)
 return;
 // there is something already on this cell, quit.
 if (this.state.board[id])
 return;
 // Copy state temporarily
 var clone = JSON.parse(JSON.stringify(this.state.board));
 // Store a move into the state.
 if (this.turn == 0) {
 clone[id] = 'X';
 this.setState({board: clone});
 this.turn = 1;
 }
 else {
 clone[id] = 'O';
 this.setState({board: clone});
 this.turn = 0;
 }
 }
 // Function to check if there is a winner.
 checkWinner() {
 var i = 0;
 // Horizontal check
 for(i = 0; i<9; i+=3)
 {
 if (this.state.board[i] == this.state.board[i+1] && this.state.board[i+1] == this.state.board[i+2] && this.state.board[i])
 return this.state.board[i];
 }
 // Vertical check
 for(i = 0; i<3; i++)
 {
 if (this.state.board[i] == this.state.board[i+3] && this.state.board[i+3] == this.state.board[i+6] && this.state.board[i])
 return this.state.board[i];
 }
 // diagonal check
 if (this.state.board[0] == this.state.board[4] && this.state.board[4] == this.state.board[8] && this.state.board[0])
 return this.state.board[0];
 if (this.state.board[2] == this.state.board[4] && this.state.board[4] == this.state.board[6] && this.state.board[2])
 return this.state.board[2]
 return -1;
 }
 // Restart the game
 restart()
 {
 // Clean board
 this.setState({
 board: [null, null, null, null, null, null, null, null, null]
 });
 this.turn = 0;
 this.gameFinished = false;
 }
 // Render
 render() {
 const that = this;
 var status = "Status: ";
 var winner;
 // Render will get called, when state is changed, so here we immediately check if there is a winner or not.
 winner = this.checkWinner()
 if (winner != -1) {
 status = "Status: we have a winner " + winner;
 this.gameFinished = true;
 }
 return (
 <div>
 <h2>{status}</h2>
 {this.state.board.map(function (currentVal, index) {
 // Draw each game cell.
 return <BoardCell which={currentVal} id={index} click={that.handleClick}
 key={index}></BoardCell>
 })}
 <button onClick={this.restart}>Restart</button>
 </div>
 );
 }
 };
 /*
 * Represents each cell in the game.
 * */
 class BoardCell extends React.Component {
 constructor(props) {
 // Call parent constructor
 super(props);
 this.click = this.click.bind(this);
 }
 click(e) {
 this.props.click(e.target.id);
 }
 render() {
 const which = this.props.which;
 const dimensions = {
 width: 150,
 height: 120
 };
 const id = this.props.id;
 if ((id + 1) % 3 == 0) {
 // If we are at the edge of right side, append also a new line
 return(
 <span>
 <img src={which == "X" ? "./resources/x.png" : which == "O" ? "./resources/o.png" : './resources/def.png'}
 style={dimensions} onClick={this.click} id={id}/>
 <br/>
 </span>
 );
 }
 else {
 // Just return image with corresponding data
 return (<img
 src={which == "X" ? "./resources/x.png" : which == "O" ? "./resources/o.png" : './resources/def.png'}
 style={dimensions} onClick={this.click} id={id}/>);
 }
 }
 };
 ReactDOM.render(
 <TicTacToe/>,
 document.getElementById('root')
 );
</script>
</body>
</html>
asked Mar 21, 2017 at 13:39
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

These are the improvements that came to my mind:

  1. Use component state to store turn and gameFinished variables so your component or it's sub components re-render when turn changes.
  2. To avoid cloning the state completely use awesome immutable.js library it is much faster and cleaner.
  3. You can use arrow functions () => in your map statement so you don't need const that = this anymore
  4. You don't need to set id attribute in your image element. In the BoardCell click method, you can simply read id from this.props.
click(e) {
 this.props.click(this.props.id);
}
answered Apr 7, 2017 at 9:03
\$\endgroup\$
1
\$\begingroup\$

These are the improvements that came to my mind:

Use component state to store turn and gameFinished variables so your component or it's sub components re-render when turn changes. To avoid cloning the state completely use awesome immutable.js library it is much faster and cleaner. You can use arrow functions () => in your map statement so you don't need const that = this anymore You don't need to set id attribute in your image element. In the BoardCell click method, you can simply read id from this.props.

answered Apr 16, 2017 at 23:18
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.