Integrating Redux

You all know that Redux made the difference few years ago and now a lot of developers are starting using it (mostly together with React).
What people really miss is that Redux can be used alone inside a frameworkless project (or inside a project that has a framework/library that is not React).

I - as senior - recently decided to introduce Redux inside an enterprise project to manage the big amount of data that frontend has to deal with. One of the perks of having Redux is to have a one-way-flow to store the data, which basically sets some rules among the team and the project itself. Having rules is good. You team decide which ones and after that you are good to go.

After the integration

So, after three weeks of refactor, coding, testing, bugfix and lot of changes among the application (they were a lot, trust me), the application ended up having almost TWO HUNDRED REDUCERS, all of the them listening to the same actions. I can’t share too much about why so many, but it was due to the project needs and architecture.
The application didn’t have any subscritions to the state changes, so there wasn’t a reactive change based on the data inside the Redux store. Everything was done to manage in a clean and fixed way the data inside the application.

Even though there was no UI involved, the number of accesses done to the state was a lot! Also, the number of actions to store the data was a lot. (I did put some logging and I was impressed).

Although, there were no performance regressions inside the application.

Further optimisations

Here’s the good part of the article. Have you ever thought of importing a reducer only when is needed? I don’t think so. You might say that there’s no need because Redux is fast and you are right. However, sometimes there are cases where is better to do it. Like this one.

Redux gives us an API called replaceReducer which is basically what is needed is this case.

It’s not well documented but it gives you an idea of when to use it.

And that’s what I did inside the code! We already had code splitting inside the application. There’s a key point about this API, the next reducer that you provide will replace the old one, which means that you have to provide a new object that contains also the old reducers.

Snippets

So, let’s put all these idea together and see how this idea could be applied to a real world scenario. First of all let’s make a function that it actually create a new root reducer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// two fake reducers for starter
const foo = (state = {}, action) => state;
const bar = (state = {}, action) => state;

// or `let` if you want to stick with functional/immutability
const currentReducers = {
foo,
bar
}

export function nextReducer (newReducers) {
// make some check here, here's is done with lodash. You could extend the check
if (_.isObject(newReducers)) {
// I'd suggest the latter approach
Object.assign(currentReducers, newReducers)
// .. or
currentReducers = {
...currentReducers,
...newReducers
}
}

return combineReducers(currentReducers)
}

So we have a snippet that gives a new root reducer with the new reducers that we want. How do we use it?
Let’s suppose that we are already inside a code splitted section of the application, for example the admin section. After the import() statement, we should behave like this:

1
2
3
4
5
6
7
8
// adminReducers.js where we have the reducers for the admin section
import { sideBar } from './sidebar'
import { settings } from './settings'

export const adminReducers = {
sideBar,
settings
}

Now let’s what’s inside the page where we actually need these reducers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { nextReducer } from 'path/to/nextReducer'
import { store } from 'path/to/my/store'

import('path/to/admin/main/page', ({ renderPage }) => {
// let's request our new reducers
requestAdminReducers()
.then(({ adminReducers }) => {
const newRootReducer = nextReducer(adminReducers)
store.replaceReducer(newRootReducer)
renderPage();
})
})

function requestAdminReducers () {
return import('path/to/adminReducers.js')
}

Voila’!

Comment and share

Redux is a library that recently got adopted by a lot of developers and companies to build rich UI applications. Redux rethinks an older library called Flux. I won’t bother you about the principals of Redux and so on, you can go to the main website and read them over there.

What I want to share is some tweak that I learned by developing using this library.

Multiple reducers

Assuming you all know what reducers are and you have a big application, you eventually use combineReducers to put all together all your reducers and create - what the official documentation calls - the rootReducer that you have to pass when you create the store.

1
2
3
4
5
6
// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer = combineReducers({
theDefaultReducer,
firstNamedReducer,
secondNamedReducer
});

There’s also a chapter of how to go beyond the combineReducers, you can access to it here.

What’s the documentation doesn’t say clearly is that you can use combine the reducers to create also nested reducers.

You all know that the shape of the store is defined by the structure of your reducers, so each reducer can be responsible of handling each part of the state.

I will show this concept with and example.

Example - User Information

Let’s say that you want to store the information of the users inside your Redux store, and the shape of it will be something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
// ...
products: {
// ...
},
cart: {
// ...
}
user: {
details: {
firstName: "Micheal",
lastName: "Jackson",
email: "m.j@gmailx.com",
eyes: "2",
description: "I am tall and white",
availability: "Every day"
},
settings: {
backgroundColor: "#fff",
receiveNotification: true,
categories: ['white', 'black', 'dance', 'sing']
}
}
}

Your redux store, other than this slice of user, will have also other slices; so other reducers.
In a ideal world you will have yours reducers imported and built in this way

1
2
3
4
5
6
7
8
9
10
import user from 'src/reducers/userReducer'
import cart from 'src/reducers/cartReducer'
import products from 'src/reducers/productsReducer'
import { combineReducers } from 'redux'

export default combineReducers({
user,
cart,
products
})

As you can see it’s a simple object. But, if you changed just one value inside the array categories, your reducer will be quite complicated because you have to maintain intact the rest of the state.

What if I told you can create nested reducers

Exactly! You can create reducers that will be responsible of a subsection of the parent reducer!

Let’s create a reducer responsible of the settings and a reducer responsible of the details

1
2
3
4
5
6
7
8
9
10
// userDetailsReducer.js

export default function userDetailsReducer (state = {}, action) {
return state
}

// userSettingsReducer.js
export default function userSettingsReducer (state = {}, action) {
return state
}

Now, the new userReducer will be something like this. We will use combineReducers to create a new reducer using the other two reducers that we just create

1
2
3
4
5
6
7
8
9
// userReducer.js
import details from './userDetailsReducer'
import settings from './userSettingsReducer'
import { combineReduers } from 'redux'

export default combineReducers({
details,
settings
})

You want to have a reducer responsible of the categories? You can follow the same pattern but bear in mind that if you did that, you would need make also backgroundColor and receiveNotification reducers!

Let’s see

1
2
3
4
// settingsCategoriesReducers.js
export default function settingsCategoriesReducers (state = {}, action) {
return state
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// userSettingsReducer.js
import categories from './settingsCategoriesReducers'

// @reducer
function backgroundColor (state = {}, action) {
return state
}

// @reducer
function receiveNotification (state = {}, action) {
return state
}

export default combineReducers({
backgroundColor,
receiveNotification,
categories
})

That’s how you create nested reducers. It’s easy and the separation of concerns is more granular and easier to understand.

Comment and share

  • page 1 of 1

Emanuele Stoppa

Programmer, Gamer and music listener


Senior UI Developer, Contractor


Dublin