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:
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:
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.useLayoutEffect
inside a component further down the tree. This can be fixed by any of the following: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.createManager
API which does the above point for youcreateManager
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>
)
}
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.
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 »