The Spread Operator . . .

A JavaScript feature spreading simplicity in our code

Imagine . . . πŸ’­

We have an array of numbers, and we want to merge it with another array. What are our options?

1. loop through each element of the first array and push it into the second

2. use built-in array methods like concat to combine them into a new array

3. . . . πŸ’‘

Much like the smooth spread of chocolate on bread, there is a simple, readable way to handle multiple elements simultaneously: the spread operator. It provides a way to expand elements of iterables like arrays, objects, or strings by taking their contents and 'spreading' them into individual items.

Let's look at several examples of how to use ...!

Spreading Arrays

We can use the spread operator to expand an array into individual elements. It’s incredibly handy for copying an existing array or merging arrays.

Cloning Arrays

const arr = [1, 2, 3];
const clonedArr = [...arr];

clonedArr -> [1, 2, 3]

Did you know? When we talk about "cloning" in programming, we refer to creating a copy of an existing structure. A true clone is independent of the original, meaning changes to the clone don't affect the original, and vice versa. Using the spread operator is a common technique to quickly clone arrays in JavaScript. At the outermost level, it effectively creates a new array, ensuring that the two arrays are distinct and won't reference the same memory space. This means that adding or removing elements from one array won't affect the other.

However, there's a catch: if the original array contains objects, those objects in both the original and cloned arrays will still point to the same memory addresses. This is what's known as "shallow copying". So, while the cloned array itself is independent, the objects inside it are not. To ensure that all nested structures are independent as well we need "deep cloning" (see Footnotes-1 for details).

Merging Arrays

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

const mergedArray = [...array1, ...array2]; 

mergedArray -> [1, 2, 3, 4, 5, 6]

The easiest way to create an array from two or more arrays!

Quick Tip: Using the spread operator to merge arrays not only provides a concise way to combine them but also ensures that the original arrays remain unchanged. Whether you're concatenating lists of numbers, strings, or other arrays, this method ensures that you get a brand-new array without altering the originals. So, the next time you're handling multiple arrays and think of chaining .concat() methods or looping through each item, give the spread operator a shot!

Spreading Objects

Spreading is not limited to arrays; we can also spread object properties. This is useful for cloning objects or merging multiple objects into one.

Cloning Objects

const person = { name: "Alice", age: 30 };
const clonePerson = { ...person }; 

clonePerson  -> { name: "Alice", age: 30 }

Heads up! Just like with arrays, using the spread operator with objects results in a shallow copy. The same issue arises with nested structures: while the top-level properties are distinct between the original and the clone, any nested objects or arrays share the same references. This means that if you modify a nested object or array in the clone, the original will be affected too. For situations requiring complete, nested duplication, consider using deep cloning techniques (see Footnote-1 for deep cloning details).

Merging Objects

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

const mergedObj = { ...obj1, ...obj2 }; 
const mergedObj -> { a: 1, b: 2, c: 3, d: 4 }

Noticed? We can combine properties from multiple objects into a new one!

Just remember though: if both objects have properties with the same key, the property from the second object (obj2 in our example) will take precedence, effectively overriding the one from the first (obj1).

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, d: 4 };

const mergedObj = { ...obj1, ...obj2 }; 
const mergedObj -> { a: 1, b: 3, d: 4 }

Overriding Properties of an Object

const obj = {a: 1, b: 2};

const newObj = {...obj, b: 3};

newObj -> {a: 1, b: 3};

Tip Time! The spread operator is not only about cloning or merging; it's also a great tool for overriding the properties of an existing object. By listing the properties to override after spreading the existing object, we can easily update specific properties without affecting the rest.

Real-life Scenario: Imagine you're working on a web application where users can customize their profiles. Let's say a user wants to update only their profile picture without changing any other information. Using the spread operator, you can easily override just the profile picture property while keeping the rest of their profile data intact.

const user = { name: "Clara", age: 25, profilePic: "oldPic.jpg" };
const updatedInfo = { profilePic: "newPic.jpg" };

const updatedUser = { ...user, ...updatedInfo };

In this example, the user's profilePic is updated, while the name and age properties remain unchanged. So convenient!

Spreading Strings

The spread operator can also be applied to strings to convert them into individual characters.

Converting String to Array

const str = "Hello";

const strArray = [...str];

const strArray -> ['H', 'e', 'l', 'l', 'o']

Yes, strings too! While we often focus on the spread operator's ability to work wonders with arrays and objects, it's also quite magical with strings. When you spread a string, each character becomes an element in an array. This can be incredibly useful for tasks like creating an array of characters to manipulate strings at a more granular level or when you're working with functions that require array inputs but you only have strings.

The Spread Operator in React

In React, the use of object and array spread operators is quite common. React state should always be updated immutably (why? see Footnote-2), meaning you should not directly modify the state object. Using the spread operator makes it easier to create a new object with the updated values.

Consider a profile settings page where you might have:

  • username

  • theme: where a user might switch between light and dark mode

  • notifications: where a user might enable or disable notifications

Now, if you have a settings page where each setting is changed individually (for example, through toggles or buttons), the React code might look like this:

const [userSettings, setUserSettings] = useState({
    username: 'Mark Twain',
    theme: 'light',
    notifications: true
});

const handleSettingChange = (settingKey, newValue) => {
     setUserSettings(prevSettings => ({
           ...prevSettings,
           [settingKey]: newValue
     }));
};

In this scenario, say you have a toggle switch to change the theme. When the user toggles the theme from light to dark, you'd call handleSettingChange('theme', 'dark').

Only the theme property of the userSettings state object changes in this case, while the other properties (username and notifications) remain the same. The spread operator ensures that the existing state values are carried over, while the [settingKey]: newValue syntax updates just the property that has changed.

An Example with a Function

Imagine you have an array of numbers and you want to find the largest number. Of course, you are thinking Math.max right away, and rightly so. Remember though, this function can only accept a list of values, and not an array. (You could then use a loop, but that would be quite tedious!)

With the powers of the spread operator and the Math.max combined, this task is a piece of cake! 🍰

const numbers = [1, 2, 3, 4, 5];

const maxNumber = Math.max(...numbers); 

maxNumber -> 5

Here the spread operator unpacks the array numbers into a list of arguments that is then used by the function. Two steps in one!

This example brings up a related subject: using . . . alongside function arguments. An interesting topic that deserves its own spotlight, so let's investigate it in a follow-up blog post.

What a treat this operator is, isn't it? 🍬

But before we wrap things up, let's have a quiz!

Quiz Time! πŸ€”

Which of the following is true? Share your answer in the comments below.

  1. I have never used the spread operator to override the properties of an object.

  2. I often use the spread operator in React when setting state.

  3. I used to think that the spread operator was a Nutella-spreading robot.

Footnotes

  1. Deep cloning ensures that every nested object or array within the primary object is also duplicated, rather than merely copying its reference. This distinction means that changes made to the cloned object, even at deeper nested levels, won't affect the original object and vice versa. It's like creating an independent twin of the original object.

    This results in two data structures that are completely separate from each other, down to the very last nested object. For JavaScript developers, this is particularly significant, as objects and arrays are reference types. If you don't deep clone, nested objects and arrays inside your clone could inadvertently affect the original data and vice versa.

    For deep cloning in JavaScript, one popular method is JSON serialization, even though it has some limitations. This approach involves converting the object into a JSON string using JSON.stringify() and then converting it back into an object using JSON.parse(). It creates a new object that is decoupled from the original. (It's essential to note that functions or undefined values will be lost in this process.)

  2. Why immutability? Immutable data structures allow React to quickly determine if changes have occurred, and that makes the re-rendering process more efficient.

    When updating the state, we're advised to embrace the principle of REPLACING, not MUTATING. The spread operator's power to override the properties of an object offers a concise and effective way to create new objects and arrays while preserving immutability.

    For more details, check out my post on Immutability in React and the React Documentation, both listed under the Resources section below.

Resources

  1. JavaScript: The Definitive Guide

    7th Edition, by David Flanagan

    O'Reilly Media, 2020

  2. Resource for JSON serialization:

    MDN Web Docs - JSON

  3. Looking to find out more about immutability in React?

    Check out my post on Immutability in React.

  4. React Documentation on Updating Objects In State

Blog post originally published on corinamurg.dev.

Β