Dealing with ((Im)mutable) Data

Emanuele Tonello

Lead Dev @ Flightsuit

It started so simple

this.setState({
  toggle: !this.state.toggle
});


this.setState({
  firstName: evt.target.value
});


this.setState({
  redirect: "/another/route"
});

Now we here

this.setState({
  first: {
    second: {
      fourth: {
        field: "newValue"
      }
    }
  }
});
let newState = this.state;

newState.first.second.fourth.field = "newValue"

this.setState(newState);
let newState = this.state;

newState.first.third.array[index] = 4

this.setState(newState);
this.state = {
  first: {
    second: {
      fourth: {
        field: ""
      }
    },
    third: {
      toggle: false,
      array: [1, 2, 3]
    }
  }
};

What is the actual problem? *

"You have no way of determining which data has changed since the previous copy has been overwritten."

* more like one of the problems

[cit. React Docs]

also...

  • deep copies and deep equals are expensive and sometimes impossible
  • hard to wrap your head around

What this talk is not about

 - not talking about data structures

 - immutable-js

 - not much about performance (maybe if we have time)

 

if you want to dig deeper into Immutability in React,

you can check this link: https://github.com/markerikson/react-redux-links/blob/master/immutable-data.md

immutability-helper to the rescue

previously 'react-addons-update'

kolodny/immutability-helper

(in line to be deprecated)

Meet your new friend

import update from 'immutability-helper';

const newData = update(myData, {
  x: {y: {z: {$set: 7}}},
  a: {b: {$push: [9]}}
});

// in React...

const newState = update(this.state, {
  x: {y: {z: {$set: 7}}},
  a: {b: {$push: [9]}}
});

this.setState(newState);

Pre-built commands

 - $push: array
 - $unshift: array
 - $splice: array of arrays
 - $set: any
> replace the target entirely.
 - $toggle: array of strings > toggles a list of boolean fields.
 - $unset: array of strings > remove the list of keys in array.
 - $merge: object > merge the keys of the object with the target.
 - $apply: function > updates the current value with the new returned value from the function.
 - $add: array of objects > add a value to a Map or Set.
 - $remove: array of strings > remove the list of keys from a Map or Set.

Some examples

const newState = update(this.state, {
  domains: {
    $push: [response.data.domain]
  },
  $merge: {
    newDomain: '',
    openAddDomain: false,
    isSubmitting: false
  }
});
const newState = update(this.state, {
  projects: {
    [index]: {
      $toggle: ["edit"]
    }
  }
});
const newState = update(this.state, {
  selectedValues: {
    $splice: [
      [
        this.state.selectedValues.length - 1,
        0, {
          from: null,
          to: null,
          depart: null
        }
      ]
    ]
  }
});

A bit beyond...

import update, {
  extend
} from 'immutability-helper';

extend('$autoArray', function(value, object) {
  return object ?
    update(object, value) :
    update([], value);
});

var state = {}

var state2 = update(state, {
  foo: {
    $autoArray: {
      $push: ['x', 'y', 'z']
    }
  }
});

// var state2 = {
//   foo: ['x', 'y', 'z']
// };

That's all folks!

@emanueletonello

emanuelet

Normalizing data with immutability-helper

By Emanuele Tonello

Normalizing data with immutability-helper

  • 544