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

I recently implemented some big optimizations for the company that I work for and I wanted to share with you how I tackled the topic step by step. The solution uses dynamic import and code splitting.

The application uses reactjs as UI library, but the concept of splitting the code can be applied to every application.

I always try to use the latest technologies and features in order to bring the highest level of quality for my customers. The application that I work on is a metadata driven application. What does that mean? It means that all the components that the application has to render will dictated by some metadata coming from the server. So by default the application doesn’t know what to render.

The application implements a mapping which is basically an object that maps the components to render:

1
2
3
4
5
6
7
8
9
10
11
12
// mapping.js
import ComponentA from 'src/path/to/ComponentA'
import ComponentB from 'src/path/to/ComponentB'

const components = {
ComponentA,
ComponentB
}

export default function getComponent(type) {
return components[type]
}

Inside the application there is a component that will be in charge of getting the component based on the definition of the metadata and it will render it. There is no rocket science behind it so I will skip this step.

Everything was synchronous and the application needed to load internally all the components before getting the first document.

Code splitting

Webpack always had a feature for code splitting called require.ensure.

I wanted to use this feature but, there is more! Webpack introduced a new approach using a function called import() which will be very soon proposed as a standard, probably inside JavaScript ES2018. If you want to have more information about how to you use the function, check this article from 2ality.

This function tells to Webpack

Get this very file specified inside the path that I give you as input and put it inside a different file that will be called ‘chunk’. Inside this chunk, also append all the dependencies that it needs

Webpack has a limitation though, the import is done at compiling time, not at runtime (there is no way at the moment to ‘guess’ which file to load). But that’s fine, I just want to implement the code splitting

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

export default function getComponent(type) {
switch (type) {
case 'ComponentA': return () => import(/* webpackChunkName: "ComponentA" */ 'src/path/to/ComponentA')
case 'ComponentB': return () => import(/* webpackChunkName: "ComponentB" */ 'src/path/to/ComponentB')
default: return undefined
}
}

You see that comment named webpackChunkName? Well, that’s a recent feature implement inside version 2.4.0 of webpack. This gives you the chance to give a name to the chunk. This feature could potentially deprecate the require.ensure.

The component that will be responsible of rendering those components will look 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
25
26
27
28
29
30
31
32
33
34
35
36
// RenderComponent.js
import getComponent from './mapping.js'

class RenderComponent extends React.Component {

constructor (props) {
super(props)
this.state = {
component: null
}
}

componentDidMount () {
const { metadata } = this.props
this.fetchComponent(metadata)
}

fetchComponent (metadata) {
const resolver = fetchComponent(metadata.type)
if (resolver) {
resolver()
.then(Component => this.setState({ component: <Component /> }))
.catch(err => throw new Error(err))
}
}

render () {
const { component } = this.state
if (!component) {
return null
}
return (
component
)
}
}

In this way, when webpack will compile the code, will create different chunks separated from the main bundle, they will have the name that I specified and will be loaded only if they are needed from the metadata!

Comment and share

  • page 1 of 1

Emanuele Stoppa

Programmer, Gamer and music listener


Senior UI Developer, Contractor


Dublin