Redux actions and reducers

We tend to split our redux code into two “sections”, an action function and a reducer function which usually uses a switch to then return state based upon the supplied action.

Actions

An action can be thought of (within Redux) as a message or something that happens (hence an action). We might write an action creator as a function, like the following

export const onLoad = (name: string) => {
  return {
    type: ActionType.Load,
    payload: name
  };
};

this returns the action which has a shape of

{ type, payload } 

The type is required (as this is what we switch in the reducer) but the payload might not be, depending upon the action.

Alternatively we might dispatch an action using the redux store, for example

store.dispatch({ type: ActionType.Load, payload: name });

The key thing when creating actions is that they should not change anything but should simply state that some action should be taken and pass any data required within the reducer.

Note: the payload can be of more complex types, usually the type is simply a string representing the name of the action.

Reducers

Reducers responds to our action messages. The usual way to write a reducer is to have a function like the following

export default (state: any = initialState, action): any => {
  switch (action.type) {
    case ActionType.Load:
      return {
        ...state,
        queryName: action.payload
      };
    // ...
  }
  return state;
}

So we simply switch, based upon the action type (hence we’d usually have the action type string as a const, which must be unique across all your application reducers, hence a style such as the following is not unusual, where we prefix the action type value with some form of namespace followed by the action name

export enum ActionType {
   Load = "myapp/load",
   // ...
}

We can clone and add different values to the state using either the spread operator, i.e.

{ ...state, queryName: action.payload }

or via

Object.assign({}, state, { queryName: action.payload }); 

Rules for reducers

  1. Must always return the state even if no changes are made an even if the state is null but must never return undefined.
  2. Treat state as immutable, hence do not change state but simply return new state (which might be cloned and with changes from the previous state).
  3. A reducer should process every action even if it just simply returns the original state.