Forward Advance Learning

A Word on Immutables

In React, state and props are what we call immutable. That means, we are never allowed to change their values. If we want to make a change, we copy the whole object into a new object, and set the new object as this.state.

Generally speaking, we will never change props directly at all.

Imagine an object:

var cat = { name: "Cantaloupe Sundae" };

Now do this:

var dog = cat;
dog.name = "Rufus";

Now if I do this, what do I get?

console.log(dog === cat);

Try it out:

See the Pen Immutables Demo by Nicholas Johnson (@superluminary) on CodePen.

Change the value of dog.name. Now alert or console.log the value of cat.name. Are they the same or different?

So immutables help us to tell if something has changed. React can use immutables to speed up change detection. If two objects are actually different objects, React can tell very quickly. This will open the door to all sorts of optimisations later.

If we mutate an object, and that object is referred to in two places, when we update one, we automatically update the other. This is potentially very bad. It's one step up from coding with globals.

Luckily, in ES6 and ES7 we have some great ways to duplicate objects without too much work. Let's look at some of them now.

Duplicating an Array

The old ES5 way to duplicate an array was with concat.

var x = [1,2,3];
y = x.concat();
(x == y) // false

This will make a full copy of the array containing all the same objects.

The newer, and simper ES6 way is with the spread operator. This works in current evergreen browsers, and can be transpiled into ES5 for older browsers with Babel.

var kittens = ["Foofums", "Special Sauce", "Tinkerbell"]
var pirates = [...cats]
kittens == pirates // false

Try it out here:

See the Pen Immutables Array Demo by Nicholas Johnson (@superluminary) on CodePen.

Create an array of TODOs. Now duplicate it and add a new todo to the top, and another to the bottom, returning a new array that doesn't affect the original array.

Duplicating an object

There are two ways to perform a shallow copy of an object. The old fashined way is to use Object.assign. This lets us make a new object with all the same attributes as the old one.

var cat = {name:'Foofums', age:12}
var unix = Object.assign({}, cat, {age:65000})
console.log(cat == unix)

The newer, better ES7 way is to use the Object spread operator. This won't work in browsers at time of writing. You'll need to run your code through Babel.

var cat = {name:'Foofums', age:12}
var unix = {...cat, age:65000}

We can go all the way with this, unpacking two objects into a new object. The second object will override the first:

var state = { name: "Twoodles", age:16 }
var newState = { age:17 }
var state = { ...state, ...newState }

Try it out:

See the Pen Immutable Objects Demo by Nicholas Johnson (@superluminary) on CodePen.

Make another duplicate of the cat object with four legs and two ears.

Shallow Copies

In all these cases, copies are shallow. Nested objects are not duplicated. This is generally speaking what you want. If a nested object has not been changed, we don't need to re-render components that depend on it.

Complex Transforms

We can combine these techniques to do more complex transformations. Here we append to a nested array:

See the Pen Immutables Complex Transformations Demo by Nicholas Johnson (@superluminary) on CodePen.

Preprend another object to the start of the array.

Here we delete the element with id of 2, using filter:

See the Pen Immutables Remove Element From Array Demo by Nicholas Johnson (@superluminary) on CodePen.

Delete all the nodes that contain the word Jam.

Here we find the todo with id=2, and replace it with a new todo with the data copied over, but different text:

See the Pen Immutables Complex Array Manipulation Demo by Nicholas Johnson (@superluminary) on CodePen.

Harder: add a status attribute to all the todo objects with the value "DONE".

Immutable State

This brings us back to our previous example. If you remember, we were modifying state directly on an object that was shared between multiple components, and this was causing our DOM to be out of sync with our models. We can fix this with immutables.

First, we will create a new copy of the hero for each Component:

this.state = {hero: {...hero}}

This might be enough, but for good measure, we can use immutables on the setState too:

let hero = this.state.hero
this.setState({
hero:{
...hero,
power: hero.power+1
}
})

Here's that in context:

See the Pen React Stateful Components Demo With Immutables by Nicholas Johnson (@superluminary) on CodePen.

Exercise - Random Name Generator

Your mision is to create an app that generates random hero names. Create them, using some random means. Use a setInterval to add a new name to the roster every three seconds. The list should never be more than ten items long, so as new ones scroll in, old ones are knocked off the bottom.

Work in this Pen (you may wish to fork it, and open in full screen)

See the Pen React Stateful Components Exercise by Nicholas Johnson (@superluminary) on CodePen.