Manipulate state in Redux, but not with ImmutableJS

Problems When Handling Redux State

  • tedious manually manipulate state
  • unnecessary state change, and component re-rendering
  • need to access state path carefully in case undefined error

Here is what we do now:

The biggest problem above is that the state will change whenever an action is fired. We can improve it like below:

This will solve the problem, and we can also use the ES6 Spread sytax to make it prettier:

Looks better, but you need to remember to do it in every case. And the code will become a lot of more cumbersome if you want to change something very nested:

Why do we need to do that, can we just?:

Either way will trigger the state to change, and will revoke your mapStateToProps method passed into the connect function, but since the state.memebers and the state.members[action.payload.id] is still using the original reference, the corresponding UI Components will not re-render, because they will treat the props passed in as “unchanged”.

Another problem we are facing is when we need to access a nested path in a object, for example:

The code above will fail if any of the path above is not exist, you have to do it like:

ImmutableJS?

The most important benefit that the ImmutableJS can give is easy reference comparision, which means, every time you make a change to a ImmutableJS Object, it will return a new reference.

So we can use ImmutableJS within our reducer:

And use setIn() in nested situations:

Simple and easy. For the access problem, we can use getIn():

Looks perfect? But when you need to use the state with your UI Component there will be problems:

Basically you will end up mix ImmutableJS up with your code everywhere, and at the end, it will hard to tell if some props are ImmutableJS Object or just a plain Object, and also tied your code with ImmutableJS.

One solution will be using toJS() in every mapStateToProps to make sure all the props passed into the UI Components are pure JavaScript Object, but it is kind of anti-patterns, because:

  • toJS() is expensive, and performance is one of the reason that you bring in ImmutableJS
  • every toJS() create new references, and you will lose the easy reference comparision that ImmutableJS gives you

So, in conclution, the key problem about ImmutableJS is that it transform the state into a Immutable Wrapper Object. It will leads to:

  • we need to use all its API to modify or get access of our data, mixed up of Immutable Object and plain JavaScript Object is inevitable.
  • we lose the functinities native Javascript Data gives us ( like ES6 Spread )
  • If will be harder to debugger the state ( since they are no longer plain object anymore )
  • The documentation may not be clear enough ( like there’s not detailed explanation for keyPath used in setIn and getIn)

references:

How About Other Immutable Helpers

At this point, if we look back the problems we want to solve, it looks like we just need some library that can:

  • help us manipulate our state easily and return a new reference
  • provide a way to access a object, like getIn()
  • keep the state as a pure JavaScript object.

So there are some Immutable Helpers that I found may fullfill these requirements:

After went through all most of the modules, I found we need a module that can provide:

  • set/get/delete an object, and can handle nested object as well
  • can give full functionalities to manipulate and get access to arrays as well
  • provide methods to merge objects
  • using structural sharing, This means it re-uses unmodified parts of your data. It will provide a better performance and also lets you compare individual parts with strict equality.
  • No unnecessary changes. Only return a new reference if an operation actually change a value.
  • The API should be easy to use.

react-addons-update from facebook looks the most official one, but its syntax is just too tedious:

Also “react-addons-update” may be deprecated in the future

object-path-immutable‘s API is very pretty and easy to use, but it lack of structural sharing and anytime you call its API, it will just return an new reference.

sprout, good API, also with structure sharing and does not do uncessary change, but lack of Array support.

So at last, I found icepick basically has everything we need, the only problem will be lack of a API to delete nested object key, but can use another API to work around this issue easily.

Also icepick froze object, so that if you try to change state directly, it will throw an error.

How Do We Manipulate State

Though we’ve decided to use Icepick as our Immutable Helper to manipulate our state, there still need a little more guide to follow when writing your reducers, below is a simple example:

For the moment, there are only two things need to notice.

First, use ES6 default parameter syntax to give your state a default value.
After the first time the reducer is invoked (with a undefined state), it will use the default value to set itself up.

The only situation may be when you need to test your reducer, and pass state into it. And since you only want to focus on part of the state, mock a whole state will be quite tedious,
So to deal with this problem, there are two things you way want to do:

  • export your defaultState from your reducer file
  • use the defaultState to merge with your partial state

Secondly, if you need to set multiple properties at the same time, you can use chain to make your code easier:

Leave a Comment

Your email address will not be published. Required fields are marked *