preface

Redux/flux requires the return of a new object to trigger data update and re-render. The general recommended method is to use object structure:

return {
  ...state,
  enabled: true,}Copy the code

If you want to change the state. The Settings. Profile. Darkmode, probably will become like this:

return {
  ...state,
  settings: {... State.settings, profile: {... state.settings.profile,darkmode: true,}}}Copy the code

There are two problems:

  1. If the state object is large (note: the object is large), the process of structuring and copying the state will take a long time
  2. The above changesstate.settings.profile.darkmodeTo take,“Huge” job

How to solve these two possible problems in using Redux is the focus of this article.

Corresponding to this article, this article contains contents:

  • Immutable data

    • New data structure
      • facebook/immutable-js
      • swannodette/mori
    • Native JS data form
      • rtfeldman/seamless-immutable
      • planttheidea/crio
      • aearly/icepick
  • Immutable Update utility

    • mweststrate/immer
    • kolodny/immutability-helper
    • mariocasciaro/object-path-immutable
    • debitoor/dot-prop-immutable
  • Immutable/Redux interoperability

    • gajus/redux-immutable
    • eadmundo/redux-seamless-immutable

Conclusion first, 80% to 90% of the scenes, just use immer.

Immutable data

facebook/immutable-js

Repository address: Facebook /immutable-js

Reference reading:

  • Intensive reading Immutable structure sharing
  • The Implementation of IMmutable.js
  • The Implementation mechanism of IMmutable. Js

In short:

  1. Immutable -js builds new data structures to solve the first problem of slow copying of large structures in a spatial-temporal manner
  2. And through thestateMap.setIn(['settings', 'profile', 'darkmode'], true)To solve the second problem

But the corresponding

  1. If the object is small, you don’t need immutable-js
  2. Be aware of the differences and operations between immutable data and native data

swannodette/mori

Warehouse Address:swannodette/mori

Should be left over from history, not to repeat.

rtfeldman/seamless-immutable

Warehouse address: rtfeldman/seamless-immutable

var array = Immutable([1.2.3]);
array.map(value= > [value+2, value+4]);
// returns Immutable([ [ 3, 5 ], [ 4, 6 ], [ 5, 7 ] ])

Immutable.flatMap(array, value => [value+2, value+4]);
// returns Immutable([ 3, 5, 4, 6, 5, 7 ])
Copy the code

Reference reading:

  • Seamless -Immutable API
  • Seamless -immutable source reading notes
  • Immutable; React; Immutable

Frozen immutable groups/objects, backward compatible JS

Immutation-js does not build new data structures. Instead, it extends javascript array and object by freezing some native array and object methods, such as pop and push

planttheidea/crio

Warehouse address: Planttheidea/Crio

An immutable JS Object with an API is essentially a seamless-immutable Object. It inherits the native Array and Object, but also overwrites/encapsulates methods like push and pop. That eventually returns a new CRIo immutable array as well

  • CrioArray pop
  • CrioArray push

This is different from the native [].push and [1, 2].pop methods. Note that these two methods return the length of the array, which introduces differences that feel more difficult to control and do more harm than good.

// you can assign with crio() directly
const crioArray = crio(['foo']);
const updatedCrioArray = crioArray.push('bar');

const crioObject = crio({foo: 'bar'});
const updatedCrioObject = crioObject.set('bar'.'baz');

// or use the convenience methods
const otherCrioArray = crio.array(['bar']);
const updatedOtherCrioArray = otherCrioArray.push('bar');

const otherCrioObject = crio.object({bar: 'baz'});
const updatedOtherCrioObject = otherCrioObject.set('bar'.'baz');
Copy the code

Reference reading:

  • Difference with seamless-immutable?
  • Why not just use X immutable library?

aearly/icepick

Warehouse address: aearly/icepick

Another wheel. It’s almost like seamless-immutable. Maybe the difference is that this is a utility function like LoDash. Seamless -immutable is the object-oriented way.

Note that it is internally similar to seamless-immutable, creating new objects via object shallow copy, slice array, and planttheidea/crio via inheritance and re-new.

var coll = {a: 1.b: 2};
var newColl = icepick.assoc(coll, "b".3); // {a: 1, b: 3}

var arr = ["a"."b"."c"];
var newArr = icepick.assoc(arr, 2."d"); // ["a", "b", "d"]
Copy the code

Reference reading:

  • Why not just use Immutable.js or mori?
  • How does this differ from React.addons.update or seamless-immutable.

Combined with these five libraries, Facebook/imMUTation-js solves the two problems mentioned above, but is relatively heavy.

And these three, because all use native JS data structure, relative, actually solve the second problem above, the significance is not very big.

  • rtfeldman/seamless-immutable
  • planttheidea/crio
  • aearly/icepick

In addition, the number of star in the latter two warehouses is small. Of course, the specific codes are not carefully studied, so specific problems and specific scenarios need to be analyzed to make the corresponding technology selection.

But is it really necessary to introduce immutable data structures if the first problem is not obvious, just to solve the second problem? To remember the corresponding new object, array new method?

Immutable Update utility

This part simply deals with the second problem, without new data structures and data objects. Four libraries are included:

  • debitoor/dot-prop-immutable
  • kolodny/immutability-helper
  • mariocasciaro/object-path-immutable
  • mweststrate/immer

Recommend mweststrate/immer directly

dot-prop-immutable

Storage address: debitoor/dot-prop-immutable

Just some helper methods.

var dotProp = require('dot-prop-immutable');
var state = { todos: [] }, index = 0;

// Add todo:
state = dotProp.set(state, 'todos', list => [...list, {text: 'cleanup'.complete: false}])
// or with destructuring assignmentstate = {... state,todos: [...state.todos, {text: 'cleanup'.complete: false}};//=> { todos: [{text: 'cleanup', complete: false}] }

// Complete todo:
state = dotProp.set(state, `todos.${index}.complete`.true)
// or with destructuring assignmentstate = {... state,todos: [
	...state.todos.slice(0, index), {... state.todos[index],complete: true},
	...state.todos.slice(index + 1)]};//=> { todos: [{text: 'cleanup', complete: true}] }

// Delete todo:
state = dotProp.delete(state, `todos.${index}`)
// or with destructuring assignmentstate = {... state,todos: [
	...state.todos.slice(0, index), ... state.todos.slice(index +1)]};//=> { todos: [] }
Copy the code

kolodny/immutability-helper

Warehouse address: Kolodny /immutability- Helper

Just some helper writing ($method)

import update from 'immutability-helper';

const newData = update(myData, {
  x: {y: {z: {$set: 7}}},
  a: {b: {$push: [9]}}});const initialArray = [1.2.3];
const newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

const collection = [1.2, {a: [12.17.15]}];
const newCollection = update(collection, {2: {a: {$splice: [[1.1.13.14]]}}});
// => [1, 2, {a: [12, 13, 14, 15]}]


const obj = {a: 5.b: 3};
const newObj = update(obj, {b: {$apply: function(x) {return x * 2; }}});// => {a: 5, b: 6}
// This is equivalent, but gets verbose for deeply nested collections:
const newObj2 = update(obj, {b: {$set: obj.b * 2}});
Copy the code

mariocasciaro/object-path-immutable

Storage address: Mariocasciaro /object-path-immutable

Much the same, the helper method, returns a new data object

const newObj1 = immutable.set(obj, 'a.b'.'f')
const newObj2 = immutable.set(obj, ['a'.'b'].'f')

/ / {
// a: {
// b: 'f',
// c: ['d', 'f']
/ /}
// }

// Note that if the path is specified as a string, numbers are automatically interpreted as array indexes.

const newObj = immutable.set(obj, 'a.c.1'.'fooo')
/ / {
// a: {
// b: 'f',
// c: ['d', 'fooo']
/ /}
// }
Copy the code

mweststrate/immer

Warehouse address: Mweststrate /immer

import produce from "immer"

const baseState = [
    {
        todo: "Learn typescript".done: true
    },
    {
        todo: "Try immer".done: false}]const nextState = produce(baseState, draftState => {
    draftState.push({todo: "Tweet about it"})
    draftState[1].done = true
})
Copy the code

DraftState is passed to function as a Proxy for the original object.

Function (with side effects) directly changes draftState, and produce returns the new object.

I recommend using immer directly, after all, you are the author of Mobx, after all, you are winning awards. This is a very intuitive way to write it, and it doesn’t need to return.

Reference reading: close reading of immer.js source code

Immutable/Redux interoperability

gajus/redux-immutable

Storage address: Gajus /redux-immutable

A tool that combines IMMUTABLE js with REdux

eadmundo/redux-seamless-immutable

Storage address: eadmundo/redux-seamless-immutable

A tool that combines seamless-immutable with REdux

Since you wouldn’t necessarily use immutation-js for the first problem, the second problem would work just fine with immer, and there’s no need to use either tool.

Other reference

Redux – Ecosystems – Immutable data