State management in WebApps

WebApps use Redux as state container. The key concepts from Redux that WebApps utilize are: Actions, Reducers and Store.

The main idea is that a WebApp's state should be kept in a single store and components should use this store to retrieve information about the WebApp's current state.

Refer to WebApp boilerplate reducer and WebApp timeline search for example code.

Actions

Actions are used to send information about something that happened to the WebApp's store. An action is a plain JavaScript object that contains a type property and, typically, a property (or properties) with relevant data used to modify state. Actions are dispatched to the store using store.dispatch(). Read more about actions from the official docs.

// action object
{
   type: 'SET_NAME',
   name: 'Robin'
}

Reducers

A reducer is a pure function that updates the state of the WebApp. When an action is dispatched, it will be passed to the reducer function that will modify the state, according to the action, and return the WebApp's new state. In a WebApp, the reducer function must be returned in the reducer.js file that is placed in the root directory. Read more about reducers from the official docs or have a look at this tutorial.

// reducer.js
define(function(require) {
   'use strict';

   var _ = require('underscore');
	// a reducer takes two arguments; current state and the action dispatched
   var reducer = function(state, action) {
      switch (action.type) {
         case 'SET_NAME':
         	// the new state should always be a new object
            return _.extend({}, state, {name: action.name});
         default:
            return state;
      }
   }

   return reducer;
});

Store

The store holds the WebApps's state and should be seen as the single source of truth. A WebApp's store is initialized with the reducer function returned in reducer.js and any initial state that is passed from the render method in index.js. The store is accessible within the WebApp's client context through require('store'). State is accessed by calling store.getState() and updated by calling store.dispatch(action).

WebApp components collect what information they need from the store through the filterState function. filterState is called with store.getState() as parameter. Components provide a shortcut to listen for store updates that affect the component (state filtered in filterState) by registering a store listener in the events hash. However, it is also possible to listen for store updates via store.subscribe(callback). Components and state is thoroughly explained in the component documentation.

Read more about store from the official docs or have a look at this tutorial.

The following is an example of when state is updated from a WebApp component.

define(function(require) {
   'use strict';

   var
      _          = require('underscore'),
      Component  = require('Component'),
      store      = require('store'),
      template   = require('/template/name');

   return Component.extend({

      template: template,

      events: {
         dom: {
            'click [data-update-name]': 'handleUpdateName'
         },
         self: {
            'state:changed': 'render'
         },
         // listen for store changes that affect the component
         store: 'handleStoreUpdate'
      },

      handleUpdateName: function() {
         // dispatches an action that will be handled by the reducer
         store.dispatch({
            type: 'SET_NAME',
            name: 'WebApp'
         });
      },

      handleStoreUpdate: function(newState) {
         // update component's local state when store is updated
         this.setState(newState);
      },

      filterState: function(state) {
         // filters a component's local state from the store
         return _.extend({}, {name: state.name});
      }
   });
});

Advanced

In order to take advantage of more advanced Redux functionality, retrieve the Redux instance within the WebApp's client context through require('redux').

See redux.js