Proposal: action filter utility #912

Closed
davidkpiano opened this Issue Oct 18, 2015 · 5 comments

Comments

2 participants
@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?

@omnidan

This comment has been minimized.

Show comment
Hide comment
@omnidan

omnidan Oct 18, 2015

Collaborator

Just like https://github.com/rackt/redux/pull/658, this would probably be better as an external reducer enhancer. I was actually thinking about writing a small library like redux-undo for this use case.

Collaborator

omnidan commented Oct 18, 2015

Just like https://github.com/rackt/redux/pull/658, this would probably be better as an external reducer enhancer. I was actually thinking about writing a small library like redux-undo for this use case.

@davidkpiano

This comment has been minimized.

Show comment
Hide comment
@davidkpiano

davidkpiano Oct 18, 2015

@omnidan This is what I had in mind: https://github.com/davidkpiano/estado/blob/master/src/utils/signal-filter.js (library-agnostic code, which is why it's called signalFilter instead of actionFilter, but same thing).

@omnidan This is what I had in mind: https://github.com/davidkpiano/estado/blob/master/src/utils/signal-filter.js (library-agnostic code, which is why it's called signalFilter instead of actionFilter, but same thing).

@omnidan

This comment has been minimized.

Show comment
Hide comment
@omnidan

omnidan Oct 18, 2015

Collaborator

@davidkpiano looks good 👌

It probably doesn't make sense to have it as part of the redux core, though.
I'll leave this decision to @gaearon 😛

Collaborator

omnidan commented Oct 18, 2015

@davidkpiano looks good 👌

It probably doesn't make sense to have it as part of the redux core, though.
I'll leave this decision to @gaearon 😛

@omnidan

This comment has been minimized.

Show comment
Hide comment
@omnidan

omnidan Oct 18, 2015

Collaborator

@davidkpiano you can use this as a template if you want: https://github.com/omnidan/redux-ignore - it's a little bit simpler (takes an array instead of a function). Maybe you could even send me a PR so it accepts both, arrrays and functions.

Collaborator

omnidan commented Oct 18, 2015

@davidkpiano you can use this as a template if you want: https://github.com/omnidan/redux-ignore - it's a little bit simpler (takes an array instead of a function). Maybe you could even send me a PR so it accepts both, arrrays and functions.

@omnidan

This comment has been minimized.

Show comment
Hide comment
@omnidan

omnidan Oct 18, 2015

Collaborator

This is part of redux-ignore now.

Collaborator

omnidan commented Oct 18, 2015

This is part of redux-ignore now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment