Search code examples
reactjstic-tac-toe

React js this.setState is not working properly


I am learning React and building a tic-tac-toe game. My code is below:

var Square = React.createClass({
        getInitialState: function() {
            return { mark: this.props.mark,
                clicks: 0
             };
        },

        respondToClick: function() {    
            if(this.state.mark == '_') {    
                var nextClick = this.state.clicks + 1;

            }   
            var newMark = nextClick%2 ? 'X' : 'O';                  
                this.setState({mark: newMark, clicks: nextClick});              
        },  
        render: function(){
                return (<button className="square" onClick={this.respondToClick}>
                {this.state.mark}
                </button>
                );
        }
    });

    var Board = React.createClass({

         renderSquare: function(i) {
                return (<Square mark='_' />);
        },
        getInitialState: function() {
            return { 
                clicks: 0
             };
        },
        changeValue: function(i, val) {
                return (<Square value={val} />);
        },
        render: function() {
                const status = 'Next player: X';
                return (
                    <div>
                        <div className="status">{status}</div>
                        <div className="board-row">
                            {this.renderSquare(0)}
                            {this.renderSquare(1)}
                            {this.renderSquare(2)}
                        </div>
                        <div className="board-row">
                            {this.renderSquare(3)}
                            {this.renderSquare(4)}
                            {this.renderSquare(5)}
                        </div>
                        <div className="board-row">
                            {this.renderSquare(6)}
                            {this.renderSquare(7)}
                            {this.renderSquare(8)}
                        </div>
                    </div>
                );
        }

    });

    var Game = React.createClass({
        render: function() {
                return (
                    <div className="game">
                        <div className="game-board">
                            <Board />
                        </div>
                        <div className="game-info">
                            <div>{/* status */}</div>
                            <ol>{/* TODO */}</ol>
                        </div>
                    </div>
                );
        }
    })

    ReactDOM.render(
            <Game />,
            document.getElementById('container')
    );

In my Square component I have respondToClick method and inside the method I have:

this.setState({mark: newMark, clicks: nextClick});   

Well, somehow neither mark nor clicks get updated. I could use a callback but in my case I do not see how I can do that... Can anyone please help?


Solution

  • The issue is that you are storing your state on whose turn it is too low in the hierarchy.

    Right now you have it such that when you click on an empty square it will increment the click count to 1 and then set the mark of that square to X. This will never change because in this function

    respondToClick: function() {    
        if(this.state.mark == '_') {    
            var nextClick = this.state.clicks + 1;
            var newMark = nextClick%2 ? 'X' : 'O';                  
            this.setState({mark: newMark, clicks: nextClick});                                  
        }               
    },  
    

    you only update state once when the square is '_' and never again. If you remove that if statement you will see your count correctly increments and the mark correctly changes.

    However, the solution to your overall problem of not having the turn change so when you click a square it turns to X and when you click another it turn to O can be solved by just moving the state of whose turn it is up to the Board component's state.