Actions

ActionCreators in Flux are functions that fire off events which tell your app to update the store.

Gambit is designed to make building ActionCreators easy-as-hell. Why? Because 99% of Actions are going to need to do the same thing and you can get very sick of the same boilerplate.

What we need to do is write an ActionCreator that our app's components will use to tell the App to get users from Github that match our search and then put them in the store. Create a folder src/actions and within it a file user.js:

import UserConstants from '../constants/User';
import {
  createStagedAction,
} from 'gambit';

export const getUserSearch = createStagedAction(
  UserConstants.GET_USER_SEARCH,
  api => api.user.search,
  {
    id: (constant, { query }) => `${constant}_${query}`,
  },
);

Presto, now whenever we dispatch(getUserSearch()) the following things happen:

  • UserConstants.GET_USER_SEARCH_STARTING is fired.
  • api.user.search is called with whatever arguments we pass getUserSearch. (we'll talk about the api object soon.
  • If the api call was successful, UserConstants.GET_USER_SEARCH_DONE is fired.
  • If the api call failed, UserConstants.GET_USER_SEARCH_FAILED is fired.

That's pretty cool.

Action Params

Whenever our action creators fire an action (GET_USER_SEARCH_DONE etc) it will also pass in any arguments that it received at call time (as well as passing these through to your api method).

So the following is the case:

dispatch(getUserSearch({ query: 'bar' });
- calls api.user.search({ query: 'bar' });
- fires an action { type: GET_USER_SEARCH_STARTING, query: 'bar' }
- fires an action { type: GET_USER_SEARCH_DONE, query: 'bar' };
- fires an action { type: GET_USER_SEARCH_FAILED, query: 'bar' };

_DONE gets an additional property body which contains the return value of the api method (or the resolve value if it's a promise).

_FAILED gets an additional property response which contains the error that caused the call to fail.

createStagedAction

createStagedAction is used to create the _DONE, _STARTING, _FAILED actions as described above that call an API. It's signature is as follows:

createStagedAction(ACTION_NAME, apiMethodToCall = () => {}, opts = {});

ACTION_NAME: string The action that will be fired, _DONE etc will be appened to this string.

apiMethodToCall: (api) => methodToCall A method that is passed the api object that is passed to the apiMiddleware when the store is created (we'll talk about this latter). It should return the function that will be called after _START and before _DONE

opts.id: string | (ACTION_NAME, args = {}) => id The ID of the call. This is used internally when we want to use action blockers (more later). If a function is provided, it will be ran when the actioncreator is dispatched and will be passed the ACTION_NAME as the first param and the argument object that the ActionCreator was called with as the second.

opts.postDone: (dispatch, doneVal, args = {}) => {}
opts.postStart: (dispatch, args = {}) => {}
opts.postFail: (dispatch, error, args = {}) => {}

These are lifecycle hooks that can be used to dispatch other actions. A common use case is dispatching a router action when an api endpoint returns positively:

const createUser = createStagedAction(
  UserConstants.CREATE_USER,
  api => api.user.create,
  {
    postDone: dispatch => dispatch(goTo('/dashboard')),
  },
);

createSimpleAction

createSimpleAction is for when you need an ActionCreator to fire an action without hitting an api or creating _STARTING and _FAILED actions.

const doSomething = createSimpleAction(Constants.DO_SOMETHING);

dispatch(doSomething()); // fires Constants.DO_SOMETHING_DONE

bindCreators

bindCreatorsis a lazymans approach to writing multiple ActionCreators that all use the same constant sets (helpful for large applications).

const { createSimpleAction, createStagedAction } = bindCreators(UserConstants);
const createUser = createStagedAction('CREATE_USER', api.user.create);