Search code examples
reactjsreactjs-flux

React / Flux, setting multiple states


I'm new to react and I am using states for my data models.

I have a menu that shows a user's profile picture through state. It's state because a user can change his profile picture.

I'd like the Menu to slide in from the left, initially hidden. Hence I'd like to add the Menu's open/close status as a state as well.

I'm using the standard Flux pattern.

Here is the relevant code:

Menu.js

_onChange:function(){
    this.setState({
        opened: MenuStore.getMenuState(),
        profilePicUrl: MenuStore.getUserPic()
    })
},

componentDidMount:function(){
    MenuStore.addChangeListener(this._onChange)
}

MenuStore.js

MenuStore = assign({},EventEmitter.prototype,{
    emitChange: function() {
        this.emit(CHANGE_EVENT);
    },

    addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
    },

    ...(rest of class not shown)
})

MenuStore.dispatchToken = Dispatcher.register(function(action) {

  switch(action.type) {

    case ActionTypes.RECEIVE_USER_DATA:
       _userData = action.details;
        MenuStore.emitChange();         
        break;

    case ActionTypes.TOGGLE_MENU:
        _opened = !_opened;
        MenuStore.emitChange();
        break;

    default:
      // do nothing
  }

});

Nav.js

toggleMenu:function(){
    Actions.toggleMenu(); //in my actions.js it dispatches TOGGLE_MENU
}

render:function(){
     return <div onClick={this.toggleMenu}>My Nav button</div>
}

I guess what I find wierd, is that I am setting the state of the User's profile picture without it having changed. Is this the correct way of doing things? Or should I emit separate change events and hence use separate callbacks, so that I set the states separately?

A related question is whether React will care if I set the state of something that hasn't changed. I.e does the diffing algo ignore the user's profile pic since it hasn't changed and therefore has no effect on React? OR does the fact that I've set the state in react, implicitly tell React that 'something has changed'?

Which is the correct React / Flux pattern? Set all the states in one callback? or set all the states separately?


Solution

  • There are a few things I learnt working with React and Flux that, I hope, can improve your approach:

    Ship the whole state with the emitted event

    In your example, you are emitting an event, and then the component is asking for data:

    _onChange:function(){
        this.setState({
            opened: MenuStore.getMenuState(),
            profilePicUrl: MenuStore.getUserPic()
        })
    }
    

    I would suggest you move to a different pattern, where the store sends the whole state, no matter the event, every time it emits an event:

    case ActionTypes.TOGGLE_MENU:
        _opened = !_opened;
        MenuStore.emitChange({opened: _opened, /* [...] */});
        break;
    

    Then in your component:

    _onChange:function(snapshot){
        this.setState({
            opened: snapshot.opened,
            profilePicUrl: snapshot.profilePicUrl
        })
    }
    

    In this way your store scales up, no matter the amount of data you want to keep in the store.

    Consider using Immutable.js or shipped immutability helpers

    A related question is whether React will care if I set the state of something that hasn't changed.

    React will trigger a virtual re-render in the virtual DOM. Then, it will execute the diff algorithm. As nothing has changed, nothing will be re-rendered. You can avoid this by overriding shouldComponentUpdate:

    Invoked before rendering when new props or state are being received. This method is not called for the initial render or when forceUpdate is used. Use this as an opportunity to return false when you're certain that the transition to the new props and state will not require a component update.

    I would strongly suggest that you start using either the immutability helpers, or Immutable.js. In this way it becomes easier to manage the whole re-rendering process, as it becomes trivial to understand when something has really changed.

    Also, bear in mind that React is extremely fast. Unless you have > 100 components listening for changes, sometimes it is better to have some wasted re-render cycle, instead of writing a convoluted shouldComponentUpdate.

    Code readability vs performance

    I guess what I find weird, is that I am setting the state of the User's profile picture without it having changed.

    This is really a trade off. If you have ~100 components listening to the same store, some of them interested in just one event, others in another one, then it would be the case to have different events (you would continue to send anyway the whole snapshot). Otherwise, I would suggest to keep your code simple: just publish one event.

    Flux comes in different flavors

    There are a few libraries now that implement ideas taken from Flux. I would suggest you have a look at Reflux and, especially, Redux.