General Reducer Action Switch

It's a bit annoying to write specific action handlers for each action since most of them share the same pattern if they modify data of the same type or data structure. With the help of the multipleActionReducer (blog post here) its possible to combine multiple actions to make a custom behavior closer to where the actions are called.

I want to have an action that takes an operator and a payload describing how to modify the data by modeling the object to change. So the action would look like this: { $set: { count: 10 } } would set the key count to 10 in the state.

Implementation of $set

const actionSwitch = (state, action) => {
  const [operator, payload] = Object.entries(action).at(0);

  const [key, value] = Object.entries(payload).at(0);

  switch (operator) {
    case "$set": {
      return { 
        ...state, 
        [key]: value 
      };
    }

    default: {
      return state;
    }
  }
}

More complex update with $function

If we need to modify the state in a more complex way we can make up an operator $function that takes a function as a value. { $function: { count: f } } to calculate the next value for the action where const f = (state, action) => state.someNumber + state.count; will set count to the sum of someNumber + count .

const actionSwitch = (state, action) => {
  const [operator, payload] = Object.entries(action).at(0);

  const [key, fn] = Object.entries(payload).at(0);

  switch (operator) {
    case "$function": {
      return { 
        ...state, 
        [key]: fn(state, action);
      };
    }

    default: {
      return state;
    }
  }
}

Usage in React

const Component = () => {
  const [state, dispatch] = useReducer(actionSwitch, { email: "" });

  return (
    <input
      onChange={(event) => { 
        dispatch({ 
          $set: { email: event.target.value } 
        });
      }}
    />);
};

Continuation

These examples only works for flat states. There are thing that can be done to handle nested payloads like { $set: { count: { value: { n: 0 } } } } but that's for another article.