Skip to content

Proposal: action filter utility #912

@davidkpiano

Description

@davidkpiano

Use case: Most of the time, in a non-trivial application, I have to fetch data from many different sources or endpoints. The code for fetching this data is mostly the same; the only difference is the entity being fetched.

However, with idiomatic Redux, I would have to make separate actions with separate types for each entity. Though this burden is easily solved with flexible action creators, the burden now falls on the reducers: since the action.type is a String, we can't really create "reducer creators" unless the creator dissects the action.type string (which is a code smell).

To further illustrate, suppose we have three actions with the same shape:

  • { type: 'UPDATE_FOO', data: {...} }
  • { type: 'UDPATE_BAR', data: {...} }
  • { type: 'UPDATE_BAZ', data: {...} }

... and each one, when sent to the respective foo, bar, baz reducers, update the state in the exact same way for each of the foo, bar, and baz entities. To reduce code duplication, we might naïvely have a "reducer creator" that simplifies the action -> state reduction:

export default function myReducerCreator(entity) {
  return (state, action) => {
    let [actionType, forEntity] = action.type.split('_'); // code smell

    if (actionType === 'UPDATE' && forEntity === entity) {
      // do state update
      return {...state, ...action.data};
    }

    return state;
  }
}

Proposal

Assume I have a single common action type that handles state updates, with this shape: { type: 'UPDATE', entity, data }. Then, instead of the "reducer target" being dependent on the action type, it can just be filtered via composition instead:

import { actionFilter } from 'redux';

function fooReducer(state, action) {
  switch (action.type) {
    case 'UPDATE':
      return {...state, ...action.data};
    default:
      return state;
  }
}

export default actionFilter((a) => a.entity === 'foo')(fooReducer);

This way, I can have a common action that can be shared in many contexts, and this provides a more scalable way of grouping actions for their respective entities.

The actionFilter takes the signature (Function filter -> Function reducer) -> Function reducer (or Function filter -> Function reducer -> Function reducer when curried, as above).

I feel this would be a very useful addition to redux/utils. What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions