At the beginning of my Hacker School batch I paired with Jake on a Javascript implementation of Conway’s Game of Life, as a way to learn Javascript and HTML5 canvas:

Game of Life with JS using HTML5 canvas
Game of Life with JS using HTML5 canvas

But our code felt a bit hacky, using global state all over the place. For example, the draw function here calls exports.game.update(), which relies entirely on side effects to update the game state:

var draw = function() {
  var update = function(){
    exports.game.update();
    renderGrid(exports.context, exports.game.grid, exports.WIDTH, exports.HEIGHT);
  };
  exports.intervalId = setInterval(update, 100);
};

So after doing a code review with Mary I refactored it into a more functional style to eliminate as much global state as I could. Instead of exports.game.update(), I created a stepGame function that takes the current game state and returns a new one:

var startAnimate = function() {
  var update = function(){
    gameState = stepGame(gameState);
    renderGrid(ctx, gameState, canvas.width, canvas.height);
  };
  intervalId = setInterval(update, 100);
};

Then I wondered how you’d implement the same interface using D3.js, so Damian and I refactored it again using SVG graphics managed by D3:

Game of Life with D3.js
Game of Life with D3.js

With D3 you treat the DOM kind of like a database, selecting groups of SVG elements and transformations on them:

base.render = function() {
  _svg
    .selectAll('g')
    .selectAll('rect')
    .attr('fill', function(d,i) {
        if (d[2]) return 'black';
        else return 'white';
        })
  return base;
}

Around that time I heard about Facebook’s React.js framework, and Thomas, a Hacker School alum who knew React, happened to be visiting that week. So I paired with him and refactored the code yet again to use React for managing the SVG elements:

GOL in React.js (using SVG elements)
GOL in React.js (using SVG elements)

Now the GOL grid was a React component (a Board), containing individual Cell components. Each step the board is created entirely anew with fresh cells whose color is determined by the state of the Board component:

var Board = React.createClass({
  ...
  render: function() {
    var a = [];
    for (var row=0; row<50; row++) {
      for (var col=0; col<50; col++) {
        a.push(<Cell
               dim='10' col={col} row={row}
               key={row + ',' + col}
               fill={this.state[[row, col]].val === 1 ? 'black': 'white'}
               handleCellClicked={this.handleCellClicked}
               />);
      }
    }
    return (
      <div>
        <input id="startButton" type="button" value="start" onClick={this.handleStart}/>
        <input id="stopButton" type="button" value="stop" onClick={this.handleStop}/>
        <svg>
          {a}
        </svg>
      </div>
    )
  },
  ...
});

Every time the Board component’s render method is called, React sends the returned elements to the virtual DOM it maintains internally. The vDOM is inspected for the actual differences that warrant DOM mutations between invocations of getNextAnimationFrame, using an optimized algorithm for diffing the vDOM tree.

I was really curious to see the performance difference between a naive Javascript implementation and the React implementation. So I wrote a Chrome browser extension that highlights DOM elements being dynamically changed on any web page:

DOM viz Chrome extension highlighting DOM elements being mutated
DOM viz Chrome extension highlighting DOM elements being mutated

For an apples-to-apples comparison of pure Javascript and React, I re-implemented the pure Javascript version again, this time using divs for each cell on the grid. The updateGrid function now adds a fresh div of the appropriate color for all the cells on the grid:

var updateGrid = function(gameState) {
  newGrid();
  var row, col, cellId, rowColDiv;
  for (row = 0; row < gameState.dim; row ++) {
    for (col = 0; col < gameState.dim; col ++) {
      cellId = "r" + row + "c" + col;
      rowColDiv = document.getElementById(cellId);
      if (gameState.grid[row][col].val === 1) {
        rowColDiv.style.backgroundColor = "black";
      } else if (gameState.grid[row][col].val === 0) {
        rowColDiv.style.backgroundColor = "white";
      }
    }
  }
};

… where the newGrid function wipes the old grid and re-adds fresh divs each time:

var newGrid = function() {
  var row, col, rowDiv;
  gameGrid = document.getElementById("grid");
  // remove existing rows
  if (document.getElementById("r" + 0)) {
    for (row = 0; row < gridSize; row ++) {
      rowDiv = document.getElementById("r" + row);
      gameGrid.removeChild(rowDiv);
    }
  }
  ...
  gameGrid.style.width = divSize * gridSize;
  // add 50 child divs for each row, with row ids 0-49
  for (row = 0; row < gridSize; row ++) {
    rowDiv = document.createElement('div');
    ...
      rowDiv.appendChild(rowColDiv);
    }
    gameGrid.appendChild(rowDiv);
  }
};

Then I did conceptually the same implementation in React, only I’m adding fresh Cell components to the vDOM each game step, where the Cell component is really just a wrapper around a div (as before it was just a wrapper around an svg rect):

  var Cell = React.createClass({
    render: function() {
      var dim = this.props.dim;
      var col = this.props.col;
      var row = this.props.row;
      var leftAlignQ = col === 0 ? true : false;
      var style = {
        width: 10,
        height: 10,
        backgroundColor: this.props.color,
        border: "solid gray 1px",
        "float": "left",
        clear: leftAlignQ ? "both" : "none"
      };
      return (
        <div width = {dim} height = {dim} style = {style} >
        </div>
      );
    }
  });

At each step, the Board component is created anew with a fresh set of Cell components:

  var Board = React.createClass({
    startAnimate: function() {
      var update = function(){
        this.setState(stepGame(this.state));
      }.bind(this);
      intervalId = setInterval(update, 100);
    },

    stopAnimate: function() {
      clearInterval(intervalId);
    },

    getInitialState: function () {
      return {grid: randomGameGrid(gridSize)};
    },
    render: function() {
      var a = [];
      for (var row=0; row<50; row++) {
        for (var col=0; col<50; col++) {
          a.push(<Cell
                 dim = '10' col = {col} row = {row}
                 color = {this.state.grid[row][col].val === 1 ? "black" : "white"}
                 />);
        }
      }
      return (
        <div>
          <input id="startButton" type="button" value="start" onClick={this.startAnimate}/>
          <input id="stopButton" type="button" value="stop" onClick={this.stopAnimate}/>
          <div width= {divSize * gridSize}>
            {a}
          </div>
        </div>
      )
    }
  });

Since I’m creating a new div for each cell on each time step in the pure Javascript version, the DOM mutation visualizer highlights every cell on every step:

Pure Javascript GOL where each cell is a div
Pure Javascript GOL where each cell is a div

But in the React version, only the Cell components whose data actually change from step to step are updated in the DOM:

React.js GOL where each cell is a React `Cell` component containing a div
React.js GOL where each cell is a React `Cell` component containing a div

This is an apples-to-apples comparison between Javascript and React, because I’m re-creating a fresh set of cells at each game step in both cases. In the pure JS version, I’m dumping a whole new set of divs into the DOM on each step. In the React version, I’m dumping a whole new set of React components in the vDOM on each step.

I remember Brian explaining this idea to me, that with React you just dump all new components into the vDOM on every step, and let React take care of figuring out what needs to change in the DOM. That was when I felt like I was starting to get the point of React, and why it’s powerful. I hope this little Game of Life demo makes that idea clear for others as well.