Search code examples
javascriptreactjsflux

My ReactJS flux App has too many 'actiontype' constants, how can I separate and namespace them?


I'm building a flux app that involves many different types of data, and the CRUD style modification of the resources. This leads to the a large number of ActionTypes. Most of them follow the same pattern, REQUEST_ENTITY, REQUEST_ENTITY_SUCCESS, REQUEST_ENTITY_ERROR, and so on.

How do I separate them into namespaced constants?

Ideally instead of accessing them like:

ActionTypes.REQUEST_ENTITY

I could access them in a more sane way like,

ActionTypes.entity.REQUEST

Solution

  • You could simply merge multiple objects (perhaps exported from different files) into ActionTypes.

    // entity_actions.js
    module.exports = {
      entity: {
        REQUEST: "entity.REQUEST",
        DELETE: "entity.DELETE",
      }
    };
    
    // user_actions.js
    module.exports = {
      user: {
        REQUEST: "user.REQUEST",
        DELETE: "user.DELETE",
      }
    };
    
    // actions.js
    var entityActions = require("./entity_actions");
    var userActions = require("./user_actions");
    var ActionTypes = Object.assign({}, entityActions, userActions);
    

    You can use something like Underscore#extend or object-assign if Object.assign isn't available in your environment.


    I personally use a small module I called nestedKeyMirror that takes a big nested object and automatically generates values based on the nesting:

    function nestedKeyMirror(obj, namespace) {
      namespace = namespace || [];
      for (key in obj) {
        if (obj.hasOwnProperty(key) && obj[key] === null) {
          obj[key] = namespace.concat([key]).join(":");
        } else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
          obj[key] = nestedKeyMirror(obj[key], namespace.concat([key]));
        }
      }
    
      return obj;
    }
    

    For example, in one app, I have the following action types defined:

    var actionTypes = nestedKeyMirror({
      LAYOUT: {
        RESIZE_PANE: null
      },
    
      CANVAS: {
        SET_PROPERTY: null
      },
    
      SHAPES: {
        ADD: null,
        SET_PROPERTY: null,
        SEND_BACKWARD: null,
        SEND_FORWARD: null,
        SEND_TO_BACK: null,
        SEND_TO_FRONT: null
      },
    
      SELECTION: {
        SELECT: null,
        DESELECT_ALL: null
      },
    
      HISTORY: {
        ADD: null,
        SELECT_INDEX: null
      }
    });
    

    This would give, e.g., actionTypes.SHAPES.ADD with an automatically-generated string value of "SHAPES:ADD". This technique can be combined with the object-merging strategy, above, to easily create deeply nested action type constants.

    [Update: it looks like someone recently released a package that does the nested key mirroring on npm: keymirror-nested]


    If the problem is that all your action types look similar, you could easily create a function to generate them (ES6 computed property syntax used here):

    function generateActionType(type, base) {
      return {
        [base]: `${base}_${type}`,
        [`${base}_SUCCESS`]: `${base}_${type}_SUCCESS`,
        [`${base}_ERROR`]: `${base}_${type}_ERROR`
      };
    }
    
    ActionTypes.entity = {};
    Object.assign(ActionTypes.entity, generateActionType("ENTITY", "REQUEST"));
    Object.assign(ActionTypes.entity, generateActionType("ENTITY", "DELETE"));
    
    ActionTypes.entity.REQUEST_SUCCESS === "REQUEST_ENTITY_SUCCESS";