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

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

In the previous article I made a superficial introduction to webpack, what is its purpose and how to set it up with the minimum configuration.

Here I want to share some of the problems that I had and how I solved them.

Webpack is really fast and the guys that develop it always try to hit the benchmarks in terms of speed of the builds but sometimes it can’t keep it, especially when your project has tons of modules that it has to process, decide the dependency order, fetch, etc. At some point you could start see some slowness in your build, especially in development mode. When you work on your dev server, you want you build to be fast, hot reload your code as fast as possible. When I worked on a code base of around 2.000 files, every save change was taking around 10 seconds to hot reload, which was affecting the efficiency of the developers in a bad way.

DllPlugin

Don’t get fooled by its name, it doesn’t have anything in common with Windows! This plugin creates a library (JSON file) that will be used by webpack to create a set of references to your modules.

This plugin is meant to be used to build once in a while the vendors of the application. Why? The vendors are less likely to change during the developments of the application, webpack doesn’t need to build them every time that we change one single line in our code base. I repeat, webpack is really fast and creates lot of optimizations, you want to use this plugin we you start seeing the new rebuild of your application around two seconds.

I use this plugin in development mode; you can’t use in production mode.

Here’s the code to set up your DllPlugin with your vendors. First of all I create two different config files for webpack. One for the vendors and one for your development environment.

1
2
3
4
5
6
7
8
9
// vendors.js
module.exports = [
'babel-polyfill'
'react',
'lodash/isEmpty',
'redux'
'redux-saga',
'redux-saga/effects',
]
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
// webpack.config.vendors.js
const webpack = require('webpack')
const path = require('path')

module.exports = {
entry: path.resolve(__direname, 'vendors.js'),
output: {
// the name of the output file
filename: 'vendors.bundle.js',
// we will store it inside the 'src' folder
path: path.join(__dirname, 'src'),
// the library name is important. Webapck will use this
// name to create all the references to this file and the
// manifest generated
library: '[name]_lib'
},
plugins: [
new webpack.DllPlugin({
// The path to the manifest file which maps between
// modules included in a bundle and the internal IDs
// within that bundle
path: './[name]-manifest.json',
// The name of the global variable which the library's
// require function has been assigned to. This must match the
// output.library option above
name: '[name]_lib'
}),
]
}

DllReferencePlugin

In order to read this vendor, we have to use another plugin called DllReferencePlugin. Below a simple snippet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// webpack.config.dev.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
entry: path.resolve(__dirname, 'src/app/index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'src'),
},
plugins: [
new webpack.DllReferencePlugin({
context: '.',
manifest: require('./vendors-manifest.json')
}),
]
}

Remember to import your vendors bundle before the bundle of your application.

1
2
3
4
5
6
7
8
9
10
11
12
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script src='vendors.bundle.js'></script>
<script src='bundle.js'></script>
</body>
</html>

It is worth to have a look inside the manifest that the plugin generates. As you can see there are different references that webpack will use to speed your build. That means, de facto, that your vendors will be built only once! So, if you upgrade your vendors or you install new vendors, remember to update your manifest again or you won’t see the changes applied to your vendors.

PrefetchPlugin

When I was working on a large scale project, the DllPlugin didn’t help that much, unfortunately. The application scale was really big, the quantity of HOF (High-order Functions) was that high that it was asking to webpack lot of computatino, especially retrieving the path of each file every time it was running the Hot Reloading. That’s where I decided to introduce the PrefetchPlugin. The link point to the old version of webpack because the new website doesn’t provide a documentation of this plugin yet.

The plugin basically puts into memory the resolution of a specific file so, when when webpack has to resolve it again, it already knows where to go. The usage of this plugin sped up the build drastically.

The thing is that I had to provide tons of files and in order to do that, I came up with a code like this

Once you have the lists of files inside your array, just follow the snippet:

1
2
3
4
5
6
7
8
9
10
11
12
 const webpack = require('webpack')
const plugins = []
files.forEach(file => {
plugins.push(new webpack.PrefetchPlugin(file));
})

module.exports = {
// ...
plugins: [
...plugins
]
}

I hope this tips would help you to help your developement environment

Comment and share

  • page 1 of 1

Emanuele Stoppa

Programmer, Gamer and music listener


Senior UI Developer, Contractor


Dublin