Skip to content

Transducers in Redux #176

Closed
Closed
@gaearon

Description

@gaearon

Reusing Store functionality is something Flux has traditionally been very bad at, due to Stores not being composable. For example, I do silly things in Flux React Router Example just to reuse the pagination code in different Stores.

The reason for this being hard in Flux is because each Store is an event emitter, and composing event emitters is hard. But in Redux, “Stores” (reducers, really!) are pure functions, and pure functions are easy to compose!

This gist shows how reducers can be easily composed. But it's still manual composition. It's expressive, but there is some boilerplate for repetitive tasks. For example, you might want to implement things like optimistic updates, undo/redo or pagination scoped to specific parts of your state, but then you can't do it just once on the top—you need to repeat this logic in every relevant reducer.

Higher order functions to the rescue! In fact, we already have a higher-order reducer: composeStores (to be renamed composeReducers). It takes your app's reducers and returns a reducer that composes their state into a single object.

We can express more patterns with higher order reducers? Absolutely! In fact this may be the beginning of reusing logic in Flux because these higher order reducers don't need to know about specific actions in your app.

Consider this:

function whitelistActions(reducer, actionPredicate) {
  return (state, action) => actionPredicate(action) ? reducer(state, action) : state;
}

It's an example higher order reducer. Its purpose is a performance optimization by “cutting off” some expensive reducers from some unrelated actions.

const reducer = composeReducers({
  entities: composeReducers(entitiesReducers),

  // performance optimization!
  editor: whitelistActions( 
    composeReducers(editorReducers),
    action => action.type.startsWith('EDITOR_')
  )
});

This is just an example of what higher-order reducers can do. They're not tied to your particular app. You can pass particular action types as parameters to them so they know which precisely actions to handle.

You can user higher-order reducers to implement things like:

  • undo/redo
  • pagination
  • optimistic updates
  • performance optimization
  • throttling actions
  • etc

in a generic way, reusable across the Redux ecosystem.

And guess what? We didn't invent higher-order reducers! They are precisely the same thing as transducers in ClojureScript.

Anybody want to take a stab at writing some transducers for Redux?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions