Skip to content
Diego Haz edited this page May 19, 2017 · 14 revisions

Actions are plain objects dispatched by the application (usually from containers) that describe changes on the store. Reducers and sagas listen to it so they can perform state changes and side effects, respectively, within the store.

A simple action looks like:

{
  type: 'RESOURCE_UPDATE',
  payload: {
    needle: 1, 
    data: {
      title: 'Hi!',
    },
  },
}

ARc uses action type constants and action creator methods to create those action objects following the Flux Standard Action pattern (FSA):

// src/store/resource/actions.js
export const RESOURCE_UPDATE = 'RESOURCE_UPDATE'

export const resourceUpdate = (needle, data) => ({
  type: RESOURCE_UPDATE,
  payload: {
    needle,
    data,
  },
})

That way, other parts of the app, usually containers, can dispatch that action:

// src/containers/ResourceList.js
import { resourceUpdate } from 'store/actions'

store.dispatch(resourceUpdate(1, { title: 'Hi!' }))

Actions are APIs

Actions are the way for containers to do things on the store. Ideally, when writing containers, the only things you will need to know is the action creator signature (resourceUpdate(needle, data)) and how to access the state through selectors (fromResource.getList(state)). What happens in the middle (sagas, reducers, API calls etc.) doesn't matter.

Consider the following action creator:

// bad
const resourceUpdate = payload => ({
  type: RESOURCE_UPDATE,
  payload,
})

The problem with that is that when dispatching resourceUpdate from containers you don't know what you need to pass to payload. You will need to read sagas and/or reducers to figure it out. It's a good practice to provide the API on the action creator signature:

// good
const resourceUpdate = (needle, data) => ({
  type: RESOURCE_UPDATE,
  payload: { 
    needle, 
    data, 
  },
})

Async actions

ARc uses redux-saga-thunk so we can get promises when dispatching "pseudo-asynchronous" actions handled by sagas. It's necessary for cases when we need to wait for an action to complete its side effects before doing something:

store.dispatch(resourceUpdateRequest(1, { title: 'Hi!' })).then(...).catch(...)

For instance, we are using this on the PostForm container (see the code).

See instructions on redux-saga-thunk to know how to do that.

Naming conventions

Actions and action creators don't perform any change by themselves. Therefore, it doesn't make sense to name them imperatively like updateResource. If it was the case, it would be more descriptive to name it like createResourceUpdateAction.

That said, to make things simpler, but stay descriptive, ARc follows these naming conventions:

  • Action types should be named as MODULE_ACTION. e.g. RESOURCE_UPDATE, RESOURCE_UPDATE_REQUEST;
  • Action creators should have the same name as their action type, but camelCased. e.g. resourceUpdate, resourceUpdateRequest.

This way, we can read it like "store dispatches a resource update request on resource 1 changing its title to 'Hi'"!

store.dispatch(resourceUpdateRequest(1, { title: 'Hi!' }))

Unit testing actions

As pure functions, action creators are very easy to unit test:

test('resourceUpdateRequest', () => {
  expect(resourceUpdateRequest(1, { title: 'Hi!' })).toEqual({
    type: 'RESOURCE_UPDATE_REQUEST',
    payload: {
      needle: 1,
      data: { 
        title: 'Hi!',
      },
    },
  })
})
Clone this wiki locally