Immutable data structures can be deeply compared in no time. This allows us to
efficiently determine if our components need to rerender since we know if the
props
changed or not!
Check out the official documentation for a good explanation of the more intricate benefits it has.
In our reducers, we make the initial state an immutable data structure with the
fromJS
function. We pass it an object or an array, and it takes care of
converting it to a compatible one. (Note: the conversion is performed deeply so
that even arbitrarily nested arrays/objects are immutable stuctures too!)
import { fromJS } from 'immutable';
const initialState = fromJS({
myData: 'Hello World!',
});
To react to an incoming actions our reducers can use the .set
and the .setIn
functions.
import { SOME_ACTION } from './actions';
// […]
function myReducer(state = initialState, action) {
switch (action.type) {
case SOME_ACTION:
return state.set('myData', action.payload);
default:
return state;
}
}
We use reselect
to efficiently cache our computed application
state. Since that state is now immutable, we need to use the .get
and .getIn
functions to select the part we want.
const myDataSelector = (state) => state.get('myData');
export default myDataSelector;
To learn more, check out reselect.md
!
ImmutableJS provide many immutable structures like Map
, Set
and List
. But the downside to using ImmutableJS data structures is that they are not normal JavaScript data structures.
That means you must use getters to access properties : for instance you'll do map.get("property")
instead of map.property
, and array.get(0)
instead of array[0]
. It's not natural and your code becoming bigger, you finish by not knowing anymore if you are working with a JavaScript object or an Immutable one. While it's possible to be clear where you are using immutable objects, you still pass them through the system into places where it's not clear. This makes reasoning about functions harder.
The Record
structure tries to get rid of this drawback. Record
is like a Map
whose shape is fixed : you can't later add a new property after the record is created. The benefit of Record
is that you can now, along with others .get, .set and .merge methods, use the dot notation to access properties, which is a good point to write simpler code.
The creation of a record is less simple. You got to first create the Record
shape. With the example above, to create your initial state, you'll write :
//the shape
const StateRecord = Record({
myData: 'Hello World!',
});
const initialState = new StateRecord({}); // initialState is now a new StateRecord instance
// initialized with myData set by default as 'Hello World!'
Now, if you want to access myData
, you can just write state.myData
in your reducer code.