It Works On My Machine

Using requestAnimationFrame with React

2014-03-12

The following is a small example showing how you can use requestAnimationFrame in conjunction with React. The animation is started in the componentDidMount hook and stopped in componentWillUnmount. The request ID is stored using React's state mechanism.

var ReactRoot = React.createClass({
   getInitialState: function () {
      return {
         millis: Date.now(),
         timezoneOffset: new Date().getTimezoneOffset(),
         request: 0
      };
   },

   componentDidMount: function () {
      this.setState({
         request: requestAnimationFrame(this.tick)
      });
   },

   componentWillUnmount: function () {
      cancelAnimationFrame(this.state.request);
   },

   tick: function () {
      this.setState({
         millis: Date.now(),
         request: requestAnimationFrame(this.tick)
      });
   },

   render: function () {
      return React.DOM.div(
         {},
         Clock({
            millis: this.state.millis,
            timezoneOffset: this.state.timezoneOffset
         })
      );
   }
});

The tick function writes the current time to a state variable, and render passes it, along with the time zone offset, down to the props structure of the Clock component:

var Clock = React.createClass({
   getAngle: function (cycleTime) {
      return 360 *
             (this.getMillis() % cycleTime) /
             cycleTime;
   },

   getMillis: function () {
      return this.props.millis -
             this.props.timezoneOffset * 60000;
   },

   render: function () {
      return React.DOM.svg({
            viewBox: '-1.5 -1.5 3 3'
         },
         React.DOM.circle({
            r: 1,
            fill: 'ivory'}),
         Hand({
            length: 0.7,
            width: 0.1,
            angle: this.getAngle(12 * 60 * 60000)
         }),
         Hand({
            length: 0.9,
            width: 0.05,
            angle: this.getAngle(60 * 60000)
         }),
         Hand({
            length: 0.9,
            width: 0.01,
            angle: this.getAngle(60000)
         })
      );
   }
});

Clock in turn renders itself as SVG, with a circle as clock face. It calculates the angle for each clock hand and sends them to the respective Hand components:

var Hand = React.createClass({
   render: function () {
      return React.DOM.line({
         y2: -this.props.length,
         transform: 'rotate(' + this.props.angle + ')',
         strokeLinecap: 'square',
         strokeWidth: this.props.width,
         stroke: 'darkslategray'
      });
   }
});

In general it's a good idea to keep the state as high up in the component hierarchy as possible. In this example that's really easy, because the sub-components have no event handling.

You'd like to see what it looks like? Go right ahead!