Course: Section

Get Started with Modern React: Learn by Doing

Episode: Title

S03・V13: Immutability

Date Created: July 17th, 2019
Last Updated: 27 days ago

Objective
  • We will show you why immutability is so important in React.
Watch Video
Duration: 6m 3s

Immutability

In the previous video, we made a copy of the squares state array and mutated the copy, and then we set the squares state array to be the modified copy. We’ll now discuss immutability and why immutability is important to learn.

There are generally two approaches to changing data:

  1. The first approach is to mutate the data by directly changing the data’s values.
  2. The second approach, using the “immutability” principle, is to replace the data with a new copy which has the desired changes.

We will add some temporary code examples, and log them to the console.

Mutate Data Directly

We will start by highlighting the first approach, using mutability. We will create an object holding the score and name of a player, who we will call player “A”, and then print out the object.

const playerA = { score: 1, name: 'Jeff' };
console.log('playerA before mutation:', playerA); // Prints `playerA before mutation: { score: 1, name: 'Jeff' }`

Now, we will mutate player A’s score directly, and print out the object again.

// Mutate `playerA` directly
playerA.score = 2;
console.log('playerA after mutation:', playerA); // Prints `playerA after mutation: { score: 2, name: 'Jeff' }`

In Chrome’s JavaScript Console, you can see the value of player “A” before and after mutation.

Immutable Approach

Now, we will first highlight the second approach, using immutability. We will create an object for player “B”.

const playerB = { score: 3, name: 'Sara' };

This time, we will make a copy of player “B”, and update the new version, using object destructuring.

// Make a copy of `playerB` and update the copy
const newPlayerB = { ...playerB, score: 4 };

Now, we will print out the original player “B”, and the new version of player “B”.

console.log('playerB:', playerB); // Prints `playerB: { score: 3, name: 'Sara' }`
console.log('newPlayerB:', newPlayerB); // Prints `newPlayerB: { score: 4, name: 'Sara' }`

In Chrome’s JavaScript Console, you can see the value of player “B” and the value of its updated copy.

Benefits to Immutability

There are 3 major benefits to using “immutability”.

  1. Complex Features Become Simple

Immutability makes complex features much easier to implement. Later on, we will implement a time travel feature that allows us to review the tic-tac-toe game’s history and jump back to previous moves. This functionality isn’t specific to games. An ability to undo and redo certain actions is a common requirement in applications. Avoiding direct data mutation lets us keep previous versions of the game’s history intact, and reuse them later.

  1. Detecting Changes

Detecting changes in mutable objects is difficult because they are modified directly. This detection requires the mutable object to be compared to previous copies of itself and the entire object tree to be traversed. Detecting changes in immutable objects is considerably easier. If the immutable object that is being referenced is different than the previous one, then the object has changed.

  1. Determining When to Re-Render in React

The main benefit of immutability is that it helps you build pure components in React. Immutable data can easily determine if changes have been made which helps to determine when a component requires re-rendering.

Cannot Use Mutable Approach in React

To show that we cannot use the mutable approach in React, let’s return to the handleClickEvent function. We will first comment out the immutable approach. Now, we can add a mutable way of updating the squares state array.

  const handleClickEvent = i => {
    // const newSquares = [...squares];
    // newSquares[i] = 'X';
    // setSquares(newSquares);
    squares[i] = 'X';    setSquares(squares);  };

In the browser, we will first click on the square with index 2. You will see that the UI does not update with an 'X' at index 2. Also, no error or warning is shown in the Console.

React does not know that the squares state array has changed, and therefore does not re-render. Always update state in an immutable way when using React. We will go back to the correct way of updating state.

Code Snapshot

src/index.js
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

import './index.css';

const Square = props => {
  return (
    <button
      className="square"
      onClick={props.onClickEvent}
    >
      {props.value}
    </button>
  );
};

const Board = () => {
  const initialSquares = Array(9).fill(null);
  const [squares, setSquares] = useState(initialSquares);

  const handleClickEvent = i => {
    const newSquares = [...squares];
    newSquares[i] = 'X';
    setSquares(newSquares);
  };

  const renderSquare = i => {
    return (
      <Square
        value={squares[i]}
        onClickEvent={() => handleClickEvent(i)}
      />
    );
  };

  return (
    <div style={{
      backgroundColor: 'skyblue',
      margin: 40,
      padding: 20,
    }}>
      Board
      <div className="board-row">
        {renderSquare(0)}
        {renderSquare(1)}
        {renderSquare(2)}
      </div>
      <div className="board-row">
        {renderSquare(3)}
        {renderSquare(4)}
        {renderSquare(5)}
      </div>
      <div className="board-row">
        {renderSquare(6)}
        {renderSquare(7)}
        {renderSquare(8)}
      </div>
    </div>
  );
};

const Game = () => {
  return (
    <div className="game">
      Game
      <Board />
    </div>
  );
};

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

Summary

We showed you why immutability is so important in React.

Next Up…

In the next video, we will enable taking turns in our game.