Tag Archives: flux

Flux & React Best Practices

So, you want to start a new javascript project using React, perhaps using the Flux architecture. Excellent! But you’re not quite sure where to begin. Well, actually, you know where to begin, but you’ve quickly hit a barrage of design questions. Should that API call go in the store, the view component, or the action creator? Should I store that data in a state or pass it down from a parent component through props? How should I update the view before I receive that ajax data? And so on.

Here are some helpful tips which can guide you along the way. I am no authority on React, so I have included references to those who are. Please leave a comment if you think there are better ways.

Components & State

Try to keep as many of your components as possible stateless.

A common pattern is to create several stateless components that just render data, and have a stateful component above them in the hierarchy that passes its state to its children via props.

State should contain data that a component’s event handlers may change to trigger a UI update.

https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html

Ajax APIs

In Flux, all ajax API calls should come from the action creators.

paraphrased from http://facebook.github.io/flux/docs/chat.html

In response, the API fires actions on success (eg. “receive data”) or failure (eg. “receive error”).

For example, ViewActionCreators.js:

// ViewActionCreators.js
var AppAPI = require('../apis/AppAPI');
var ViewActionCreators = {
  doSomething: function(something) {
    AppDispatcher.handleAction({
      actionType: AppConstants.DO_SOMETHING,
      something: something
    });
    // fire off API call
    AppAPI.fetchData(something);
  }
}

AppAPI.js:

// AppAPI.js
// this example uses jQuery's ajax call
var ServerActionCreators = require('../actions/ServerActionCreators');
function post(url) {
  $.ajax({url: url
    success: function(data) {
      ServerActionCreators.receiveData(data);
    },
    error: function(xhr, status, err) {
      ServerActionCreators.receiveAPIError({ err: err });
    }
  }
}

ServerActionCreators.js:

// ServerActionCreators.js
var ServerActionCreators = {
  receiveData: function(data) {
    AppDispatcher.handleAction({
      actionType: AppConstants.RECEIVE_DATA,
      data: data
    });
  },
  receiveAPIError: function(err) {
    AppDispatcher.handleAction({
      actionType: AppConstants.RECEIVE_API_ERROR,
      err: err
    });
  }
}

In this code, the user interacts with the view, which fires the DO_SOMETHING action. That action itself fires off the API call, and when the API call is done it sends either a RECEIVE_DATA or a RECEIVE_API_ERROR action, which the Store can listen for. (As we’ll see below, the Store may listen for DO_SOMETHING too.)

These snippets are based on the Flux demo Chat app.

It seems there is one exception to the rule that only Action Creators call the API: loading initial data. This can go directly in the view controller. See https://facebook.github.io/react/tips/initial-ajax.html for an example.

Stores

Stores should remain as independent and decoupled as possible — a self-contained universe that one can query from a Controller-View.
The only road into the Store is through the callback it registers with the Dispatcher. The only road out is through getter functions.
Stores also publish an event when their state has changed, so Controller-Views can know when to query for the new state, using the getters.

http://stackoverflow.com/questions/23591325/in-flux-architecture-how-do-you-manage-store-lifecycle

Stores should only execute synchronous code. Otherwise they are too hard to understand.

http://www.code-experience.com/async-requests-with-react-js-and-flux-revisited/

Immutability

If you’re using Flux, you should start writing your stores using immutable-js.

If we also use immutable-js data structures to hold the components’ state, we could mix PureRenderMixin into all our components and short circuit the re-rendering process.

https://facebook.github.io/react/docs/advanced-performance.html#immutable-js-and-flux

Here is how I’ve done it. In the Store, make changes like this:

var Immutable = require('immutable');
var selected = Immutable.List();  // was: var selected = [];
// ...
selected = selected.push(item);  // was: selected.push(item);
// ...
selected = selected.delete(i);   // was: selected.splice(i, 1);
// ...
// I receive a js array of js objects from the API
// and convert to immutable list of immutable maps: 
  var tmp = myArray.map(function(item) {
    return Immutable.Map(item);
  });
  return Immutable.List(tmp);
// ...

In the Component, there are not many changes required – I don’t even require('immutable') in this example. The changes arise just because the objects returned by the Store are no longer plain old javascript objects, but immutable ones.

var React = require('react/addons');
var PureRenderMixin = React.addons.PureRenderMixin;

// no change needed state-setting function if it's like:
function getMyState() {
  return {
    a: MyStore.getSelected(),
    b: MyStore.getOther()
  };
}

React.createClass({
  mixins: [PureRenderMixin],      // add this
  // no change to these, eg.
  getInitialState:function(){
    return getMyState();
  },
  _onChange: function() {  // ChangeListener callback
    this.setState(getMyState());
  },
  // ...
var num = this.state.a.count();  // was this.state.a.length 
// maybe other small changes due to API differences

Here is an example of the ToDo app rewritten with Flux and immutablejs.

The React documentation also refers to another immutability helper, update(). This uses ordinary Javascript objects, whereas immutablejs uses its own object types. I have decided to use immutablejs, but please tell me if you have a way to do it with update.

Optimistic Updates

While waiting for an ajax API to return, you often want to optimistically update the UI. I’ve settled on the following pattern to do this:

  • Optimistically update the Store. Do not bypass the store by only saving it in the component state.
  • This means you will update the same Store data twice: once based on the user action, and then again based on the received API action (or API error action).
  • If you need to know if the API action has completed, you can set another variable (eg. optimistic) to track that, along with a getter function that the component can listen for. I’m using this to display a spinner while some data loads.

I tried doing each of these on its own too, but then you either can’t easily update the view optimistically, or you get a messy chain of actions.

The potential downsides are:

  • There could be other reasons for the API actions being received
  • I’m not sure I want this store to know about the API-generated actions (though the alternative still needs the API error action)
  • What if an API call is not required, and the data comes from somewhere else?

Reacting to non-React events

Attach generic DOM events (eg. window resizing) to componentDidMount. This is well explained at http://facebook.github.io/react/tips/dom-event-listeners.html

In the window resizing example, note they do not attempt to keep the current window size in a Store. Instead, it is kept in the component’s state. It would be complicated to keep a Store synched up with window.innerWidth, which is anyway the ultimate truth here.

Testing components

It took me a while to get Jest tests working, due to a combination of version conflicts, trouble understanding the nature of auto-mocking, and then deciding which things to mock. I’ve written up a separate post on unit testing components.

Other resources

Here are some resources (well, one anyway) that helped me get going: