Three Dots: The Spread Operator in JavaScript and React

Three Dots: The Spread Operator in JavaScript and React

Michael Charles Aubrey // Fri May 19 2023

The JavaScript Spread Operator

The spread operator is a useful tool for breaking down array or object elements into individual pieces and expanding them in a new context.

Arrays and the Spread Operator

Let's first look at how to use the spread operator with arrays.

For example, let's say we have the following array:

let arr1 = [1, 2, 3];

When you want to spread this into a new array, you use the spread operator:

let arr2 = [...arr1, 4, 5, 6];
console.log(arr2); // Output: [1, 2, 3, 4, 5, 6]

The spread operator can also be used as arguments in function calls. Here's an example:

let numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // Output: 3

Objects and the Spread Operator

The spread operator can also be used with objects. It's useful when you want to copy properties from one object to another or add new properties to an existing object.

Here's an example with an object:

let obj1 = {a: 1, b: 2};

If you want to spread this object into a new object, you can do it like this:

let obj2 = {...obj1, c: 3};
console.log(obj2); // Output: {a: 1, b: 2, c: 3}

You can also use the spread operator to override existing object properties. For example, you can change the value of b in obj1 to 3 like this:

let obj3 = {...obj1, b: 3};
console.log(obj3); // Output: {a: 1, b: 3}

These are the basic uses of the JavaScript spread operator.

Reference Types and the Spread Operator

In JavaScript, arrays and objects are reference types, meaning they point to specific locations (references) in memory. For example, when you assign an object to another object, both reference the same data in memory. Therefore, when you modify one, it affects the other.

let obj1 = { name: "Smith" };
let obj2 = obj1;
obj2.name = "Jones"; 
console.log(obj1.name); // Jones

In the example above, even though we changed the data through obj2, the value of obj1 also changed because obj1 and obj2 share the same reference.

However, when using the spread operator, you can generate a new array or object and copy the values from the original array or object into the new one. This operation creates a new reference for the new array or object, allowing you to manipulate the new data without directly modifying the original data.

In React, state immutability is very important. Directly modifying state can prevent React from accurately tracking state changes, potentially causing unexpected bugs and performance issues. By using the spread operator to create new arrays or objects, you can avoid this problem and allow React to accurately track state changes and perform appropriate re-rendering.

Using it with React State

State in React is a built-in feature that allows components to create and manage their own data. When state values change, the component re-renders and updates the UI. State can handle anything from simple strings to complex objects and arrays. Let's explain how and why to update array and object state using Hooks and functional components, with examples using the spread operator.

Array State and the Spread Operator

Here's an example of creating array state using the useState hook and adding new items using the spread operator:

export const Products = () => {
  const [products, setProducts] = useState(["apple", "orange", "banana"]);

  const addProduct = (product) => {
    const newProducts = [...products];
    newProducts.push(product);
    setProducts(newProducts);
  };

  return (
    <div>
      <ul>
        {products.map((product, i) => (
          <li key={i}>{product}</li>
        ))}
      </ul>
      <button
        onClick={() => {
          addProduct("strawberry");
        }}
      >
        Add Strawberry
      </button>
    </div>
  );
};

We're using the useState hook to set the initial state of products (["apple", "orange", "banana"]). Then, we define an addProduct function to add new products.

The spread operator is used in this addProduct function. In the line const newProducts = [...products];, we're using the spread operator to copy the elements of the current products array into a new array newProducts. This makes newProducts a new array with the same elements as products.

Then, we add the new product to newProducts with newProducts.push(product); and set this updated array as the new state with setProducts(newProducts);.

The use of the spread operator is important here because of React's state immutability principle. Instead of directly modifying the existing state (the products array), we create a new array (newProducts) to update the state. This makes it easier for React to track state changes and efficiently re-render when state updates.

When the button is clicked, the addProduct function is called, adding "strawberry" to the product list. As a result, the product list re-renders and displays the newly added "strawberry".

Object State and the Spread Operator

Similarly, here's an example of creating object state using the useState hook and creating new objects using the spread operator:

import { useState } from "react";

export const UserProfile = () => {
  const [user, setUser] = useState({
    name: "Smith",
    age: 25,
    email: "smith@example.com",
  });

  const handleAgeChange = (newAge) => {
    const newUser = { ...user };
    newUser.age = newAge;
    setUser(newUser);
  };

  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
      <p>Email: {user.email}</p>
      <button
        onClick={() => {
          handleAgeChange(user.age + 1);
        }}
      >
        Increase Age by 1
      </button>
    </div>
  );
};

We're using the useState hook to set the initial user state and defining a handleAgeChange function. This function uses the spread operator to copy the properties of the user object into a new object (newUser). Then, we update the age in newUser to the new age (newAge) and set this new user object as the state using setUser.

Using the spread operator here is important for maintaining state immutability. By creating a new object before updating the state, React can efficiently track state changes.

When the button is clicked, the handleAgeChange function is called, increasing the user's age by 1. This updates the user profile display.

Try It Yourself: Challenges

To deepen your understanding of the spread operator, try these challenges:

Challenge 1: Array Concatenation

Given these two arrays:

let array1 = [1, 2, 3];
let array2 = [4, 5, 6];

Try using the spread operator to create a new array that combines these two arrays.

Solution

Challenge 2: Array Copying

Given this array:

let array = [1, 2, 3, 4, 5];

Try using the spread operator to create a copy of this array.

Solution

Challenge 3: Object Copying

For the final challenge, let's try copying an object using the spread operator. Given this object:

let person = { name: 'John', age: 20 };

Try using the spread operator to create a copy of the person object and then change the name to 'James'.

Solution

This article was originally published on Qiita.