Announcing redux-injectors 2.0

It’s been a long time coming, but redux-injectors 2.0 is here 🎉

For those not familiar, redux-injectors is a library that enables modular redux. Instead of giving your store a big list of reducers, redux-injectors allows pages (or components) to inject reducers (or sagas) themselves.

In redux-injectors , you can do things like this:

// managers/users.js

const UsersManager = createManager({ 
  name: "UsersManager", 
  key: "users", 
  reducer: usersReducer, 
  saga: usersSaga 
}) 
// pages/UsersPage.jsx

import { UsersManager } from '~/managers/users';

function UsersPage() {
  return (
    <UsersManager>
      <h1>Users</h1>
      <UsersList />
    </UsersManager>
  )
}

The UsersManager here will inject the users reducer when the page mounts. This allows each page (or even sections of pages) to declare which reducers (or sagas) they want to use.

There's a few reasons why you might not want to load all your reducers and sagas upfront:

  1. You don't need all the reducers and sagas for every page. This library lets you only load the reducers/sagas that are needed for the page being viewed. This speeds up the page load time because you can take advantage of code-splitting. This can also be good for performance after the page has loaded, because fewer reducers and sagas are running at any one time.
  2. You don't want to have to manage a big list of reducers/sagas. This library lets components inject their own reducers/sagas, so you don't need to worry about adding reducers/sagas to a global list. If you want to do something fancy like make a shared library of pages or containers, then this can be even more valuable.

What’s new in 2.0?

Breaking Changes

There’s unfortunately a moderately sized breaking change in 2.0

The previous version of redux-injectors injected the reducer during the first render. In react 16, this caused a warning to show up.

Warning: Cannot update a component from inside the function body of a different component

In react, renders are supposed to be pure, and there are some cases where injecting a reducer can cause a side-effect. If you have an afternoon, this thread goes into the gritty details.

To fix this, we changed reducers (and sagas) to inject during a layout effect. We think this is the best option, but it will break two existing patterns:

  1. A useSelector call after a call to useInjectReducer will result in undefined for the first render, if the selector tries to select from a piece of state that the reducer manages. If you need to do this, please make your selector resilient to undefined state.
  2. An injected reducer will not receive a dispatch if that dispatch is made inside a useLayoutEffect inside a component further down the tree. This can be fixed by any of the following:
  • Dispatch inside a useEffect instead of a useLayoutEffect
  • useInjectReducer now returns a boolean indicating whether or not the reducer has finished injecting. This allows you to return null if the boolean is false instead of the component's children.
  • Use the new createManager API which does the above point for you

createManager

There’s a new convenience API called createManager that can be used to create components that will inject a reducer or saga. It also helps avoid some pitfalls, because it only renders its children after the reducer / saga have been mounted.

// managers/books.js

const BooksManager = createManager({ 
  name: "BooksManager", 
  key: "books", 
  reducer: booksReducer, 
  saga: booksSaga 
}) 
// pages/BooksPage.jsx

import { BooksManager } from '~/managers/books';

function BooksPage() {
  return (
    <BooksManager>
      <h1>Top Sellers</h1>
      <BooksList />
    </BooksManager>
  )
}

Have fun

We think redux-injectors is the best way to implement modular redux with a light-weight API that doesn’t get in your way. Try it out and let us know what you think.

Written By Ben Lorantfy

Ben is an award-winning software developer specializing in frontend development. He builds delightful user experiences that are fast, accessible, responsive, and maintainable and helps others to do the same. He lives with his partner and dog in Kitchener, Ontario.More About Ben »Follow @benlorantfy on twitter »