Close

18th August 2014

Tic Tac React

I’m not going to go into the ‘What is React/Why use it’ – if you need some motivation as to why, read why react? and take 45 minutes to watch Rethinking web app development.

So, for this post, I’m going to walk through building a simple Tic-Tac-Toe (Noughts & Crosses to us Brits) game, no frills or AI.

The “finished” example can be seen here.

Breaking down the UI

For react apps to ‘work’ you first need to be able to break down the user interface into components.

Game Outline

Game Outline

The basic app here comprises of the Game board (red), the Tiles (green) – where the noughts and crosses go – and the Menu (blue) showing whose turn it is and a button to reset the game.

So, using the JSX syntax – the code for the three components needed are:

// View Result... 
// >> http://brian3kb.github.io/tic_tac_reactjs/01/
/**
 * @jsx React.DOM
 */
(function(){
    //The game container (red)
    var Game = React.createClass({
        render: function() {
            return <div>
                <div id='game'>
                    //game tiles will go here
                </div>
                <Menu />
            </div>;
        }
    });
    //The tile component (green)
    var Tile = React.createClass({
        render: function() {
            return <div className='tile'></div>;
        }
    });
    //The menu (blue)
    var Menu = React.createClass({
        render: function() {
            return <div id='menu'></div>;
        }
    });
    //The renderComponent method is called to render the Game component to the specified DOM element.
    React.renderComponent(
        <Game />,
        document.getElementById('container')
    );
})();

Note the @jsx React.DOM in the comment block – this must be present at the top of all files/code that needs to go through the jsx parser which then generates JavaScript from the above psuedo-DOM elements.

Putting the tiles onto the game board

So far, react is rendering the components, but its not looking much like a playable game yet.

First, the game needs an initial state so there is something to render.

// View Result...
// >> http://brian3kb.github.io/tic_tac_reactjs/02/
var Game = React.createClass({
    getInitialState: function() {
        return {
            //Initial state of the game board.
            tiles:  [
                '', '', '',
                '', '', '',
                '', '', ''
            ]
        };
    },
    render: function() {
        return <div>
            <div id='game'>
                { this.state.tiles.map(function(){
                    return (
                        <Tile />
                        );
                }, this) }
            </div>
            <Menu />
        </div>;
    }
});

Adding the getInitialState method to the Game component with a tiles array with 9 placeholder/empty tiles ready to fill with the players moves, but to show the tiles, the render method has also changed here, using curly braces, regular JavaScript can be put inline so react can do an Array.map on each of the tiles in the array that is defined in the getInitialState, this results in 9 tiles being rendered within the game board.

Adding some interaction

Clicking on a tile, should place an X or a O inside it – so lets add an onClick handler to the Tile component.

Due to the top-down data flow model that react imposes, the Tile can’t have a method that sets state on its parent (change the tiles array), the component can only affect its own state, so the actual method that modifies the Game components state must be on the Game component, so to call this from a Tile – the method needs to be handed over as a property.

// View Result...
// >> http://brian3kb.github.io/tic_tac_reactjs/03/
var Game = React.createClass({
    getInitialState: function() {
        return {
            //Initial state of the game board.
            tiles:  [
                '', '', '',
                '', '', '',
                '', '', ''
            ],
            //Noughts always have the first go.
            turn: 'o'
        };
    },
    //Tile click method to modify the state of the tiles array
    tileClick: function(position, player) {
        var tiles = this.state.tiles;
        tiles[position] = player;
        this.setState({tiles: tiles, turn: player === 'o' ? 'x' : 'o'});

    },
    render: function() {
        return <div>
            <div id='game'>
                { this.state.tiles.map(function(tile,position){
                    return (
                        <Tile status={tile} key={position} turn={this.state.turn} tileClick={this.tileClick} />
                        );
                }, this) }
            </div>
            <Menu />
        </div>;
    }
});
var Tile = React.createClass({
    //The method to handle when a user clicks on the tile, calls the tileClick method on the parent component that is referenced in the props object.
    clickHandler: function() {
        this.props.tileClick(this.props.key, this.props.turn);
    },

    render: function() {
        return <div className={this.props.status === '' ? 'tile' : 'tile status-' + this.props.status} onClick={this.clickHandler}>{this.props.status}</div>;
    }
});

An additional property to the state has been added to keep track of which players turn it is, this is alternated on each tile click.

Getting some game behaviour working

One problem showing now, is that the same tile can be overwritten, once a tile is set, it should stick until the game gets reset.

// View Result...
// >> http://brian3kb.github.io/tic_tac_reactjs/04/

//Modified tileClick method of Game component.
tileClick: function(position, player) {
    var tiles = this.state.tiles;
    //If the selected position is already filled, return to prevent it being replaced.
    if ( (tiles[position] === 'x' || tiles[position] === 'o') ) return;
    tiles[position] = player;
    this.setState({tiles: tiles, turn: player === 'o' ? 'x' : 'o'});
},
render: function() {
    return <div>
...
        //Add turn property to be passed to Menu component.
        <Menu turn={this.state.turn} />
    </div>;
}

//Modified Menu component.
var Menu = React.createClass({
    render: function() {
        return <div id='menu'>
            <h3>Player {this.props.turn}'s turn.</h3>
        </div>;
    }
});

So far so good

So far, the basic layout has been created and React’s top-down data flow can be seen in action with the Tile’s status and the current players turn getting updated in the Game’s state.

What should be apparent is the ‘free’ updating of the UI whenever a components state changes, you can see the diffing in action in the browser inspector where only the tile that changes gets updated rather than the whole DOM tree for the Game component and its children.

Part 2 of this brief intro will add the missing game logic, the curious can check out the source of the completed example in the github repo.

Leave a Reply