Why not to store objects in Redux

Here are the top 4 benefits that we experienced since we replaced all complex objects in our Redux store with plain objects.

Ronny Roeller
NEXT Engineering
3 min readJul 3, 2017

--

Background

The Redux documentation advises to store only plain objects in Redux. But when we adopted Redux, our existing codebase relied heavily on classes that provided a view over the data delivered by our backend API. For example, this wrapper class enables direct access to the primary email address of a user:

class UserWrapper {

/** Copy all backend data into the wrapper object */
constructor(dataFromBackendApi) {
Object.keys(data).forEach(key => this[key] = data[key]);
}
/** @return {String} main email address if the user has one */
get primaryEmail() {
return this.emails.length > 0 ? this.emails[0] : null;
}


...
}

We therefore had to store these wrapper objects into our Redux store. Lately, we replaced all wrapper objects with plain objects. Now, we convert the objects directly when storing them into Redux, e.g.:

/** Reducer to store one user in Redux */
const user = (user = {}, action) => {
switch (action.type) {
case 'USER/SET':
const emails = action.user.emails;
return {
primaryEmail: emails.length > 0 ? emails[0] : null,
};
...

So, what did we gain from this change?

Benefit 1: Lower memory consumption

APIs are typically designed for extendability, which leads to deep object structures, e.g.:

{
user_type: {
id: 'employee',
collaborne: {
job_title: 'Developer',
type_description: 'An employee is a person working for us.'
}
}
}

With our plain objects, we simply store this in Redux as:

{
user_type: 'employee',
job_title: 'Developer',
type_description: 'An employee is a person working for us.'

}

[Bonus: Less clicking to open nodes in Redux DevTools.]

But we can do even better: The type description is provided for each user — although it’s always the same for each employee. Instead of storing a copy of the description with each user, we can now normalize the Redux shape, and store the description for each user type only once:

{
'employee': {
description: 'An employee is a person working for us.'
}
}

The normalized user and user-type entries are then combined in the selectors as required.

Yet, the real boost came actually from somewhere unexpected: As we mapped the data provided by the backend API to our new plain objects, it became apparent that lots of data wasn’t used at all in the frontend! We drop now all those unused data points when creating the plain objects. With that, Redux contains only what is really used by the frontend.

Benefit 2: Less state changes

Our wrappers had to be replaced whenever a property changed, e.g.:

/** Reducer to store one user in Redux */
const user = (user = {}, action) => {
switch (action.type) {
case 'USER/UPDATE_EMAIL':
return new UserWrapper(Object.assign({}, user, {
emails,
});
...

This would trigger a state change for the complete user object — although e.g. only the emails property was changed. As a consequence, we had to check ourselves which of the user’s properties really changed.

With plain objects, we can now fully leverage the immutability of the Redux store. If the values didn’t change, the objects are equal. Simple as that.

Benefit 3: Redux DevTools show derived data

The DevTools allow to inspect what’s in the Redux store, and — via time travel — even what used to be in the store. But the State Inspector shows only own properties, i.e. it won’t show the derived properties like our primary email address (getter function).

Since we store plain objects in Redux: no more second guessing what the wrapper will do based on the underlying data! WYSIWYG.

Benefit 4: Save & restore state made easy

Plain objects can be easily serialized, and then restored from there. This comes in handy not only if your application has to restore its state after an application restart but also when you have to test and debug your application.

Happy coding!

Want to learn more about coding? Have a look to our other articles.

Photo: geopungo

--

--

Ronny Roeller
NEXT Engineering

CTO at nextapp.co # Product discovery platform for high performing teams that bring their customers into every decision