Telerik blogs
How ToT Light_870x220

In this article, we’re going to learn about a concept that’s widely used nowadays in JavaScript applications: immutability.

We’re going to learn more about immutability in JavaScript, how this concept can help us to write better applications, and help us manage our data, so that when we use it on a daily basis it will improve our code.

The way we’re writing code is changing pretty fast — every day we have something new being released, a new concept created, a new framework or library to help us better do a specific task. With these daily changes, we must always be learning something new — it becomes part of our job. Especially in JavaScript development, a language that evolves and changes every day with new technologies, we must pay attention to what’s really important in our applications and what should be left out, finding the right thing for the right situation.

With the rising popularity of functional programming, one of the concepts that’s trending and being talked about a lot is immutability. This concept is not exclusive to functional programming languages — we can have it in any language we want, but the concept was really brought to light and widely spread by functional programming in the JavaScript development community.

So, let’s dive into immutability, especially in JavaScript, and understand how it can help us write better applications that keep our data safer and immutable.

Concept of Immutability

The concept of immutability is pretty simple and powerful. Basically, an immutable value is something that cannot be changed. Especially when we’re developing our applications, we might end up in some situations where we want to create a new object in our code, containing a new property or value while also maintaining the original value. The concept of immutability can help us to create new objects, making sure that we’re not changing the original value.

In JavaScript, we have primitive types and reference types. Primitive types include numbers, strings, boolean, null, undefined. And reference types include objects, arrays and functions.

The difference between those types is that the primitive types are immutable (or unchangeable), and the reference types are mutable (changeable). For example, the string type is immutable:


let myAge = "22";

let myNewAge = myAge;

myAge = "23";

We just created two variables and assigned the myAge to the myNewAge variable. But after we changed the value of myAge, we will see that they’re not the same.


console.log(myAge === myNewAge); // false

const vs. let

The ES6 version allowed us to replace variables in our code with constants by using the const keyword. But a little detail that a lot of developers might not notice, is that the const keyword is not immutable.


const myName = "Leonardo Maldonado";

The const keyword only creates a read-only reference to a value, which means that the value cannot be reassigned. As the MDN reference says:

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned.

But if we try to change the value of the constant, we receive an error.


const myName = "Leonardo Maldonado";

myName = "Leo"; // Identifier 'myName' has already been declared

The ES6 version also gave us a new way to declare variables, which we can understand as the opposite of the const keyword. The let keyword allows us to create variables that are mutable, as the constants, but with this keyword, we can actually assign a new value.


let myName = "Leonardo Maldonado";

myName = "Leo";

console.log(myName) // Leo

By using the let keyword, we’re able to assign a new value. In this example, we created a let with value of Leonardo Maldonado; then we reassigned it with the value of Leo. This is the difference between let and const.

We know that JavaScript is evolving pretty fast, and with each new version of the language we’re getting more amazing features, so the consequence is that, over the years, it’s getting easier to write better JavaScript and we can achieve more with less code.

Let’s take a look now at some methods that we can start to use in our applications to help us achieve a nice level of immutability.

Objects

One of the pillars of our applications is the object. We use objects in every piece of our applications, from the front end to the back end, in the most complex component to the simplest.

Let’s imagine that we have an object called myCar, which has the following properties:


const myCar = {

model: "Tesla",

year: 2019,

owner: "Leonardo"

};

For example, we could change a property directly if we wanted to, right? Let’s change the owner of myCar.


const myCar = {

model: "Tesla",

year: 2019,

owner: "Leonardo"

};

myCar.owner = "Lucas";

But this is a bad practice! We should not change the property of an object directly — this isn’t how immutability works. As the Redux documentation recommends, we should always create a modified copy of our object and set the owner to Lucas.

But how we could do that? Well, we could use the Object.assign method.

Object.assign

The Object.assign method allows us to copy or pass values from one object to another. It returns the target object. This is how it works:


Object.assign(target, source);

  1. The method receives a parameter that’s our target, the object that we want to modify.

  2. The second parameter is our source, so we’ll merge the source object with our target object.

Let’s have a look in this example:


const objectOne = {

oneName: "OB1"

};

const objectTwo = {

twoName: "OB2"

};

const objectThree = Object.assign(objectOne, objectTwo);

console.log(objectThree);

// Result -> { oneName: "OB1", twoName: "OB2" }

Now, let’s imagine that we want to pass the values from a specific object to a new variable. This is how we would do:


const myName = {

name: "Leonardo"

};

const myPerson = Object.assign({}, myName);

console.log(myPerson);

// Result -> { name: "Leonardo" }

By doing this, we’re copying the values and properties of the myName object, and assigning it to our new variable myPerson.

Let’s imagine that we wanted to copy all the values and properties of the myName object, but we also wanted to add a new property to the myPerson object. How would we do it? Simple: by passing a third parameter and passing our new property to it, in our case the age.


const myName = {

name: "Leonardo"

};

const myPerson = Object.assign({}, myName, {

age: 23

});

console.log(myPerson);

// Result -> { name: "Leonardo", age: 23 }

Spread Operator

Another way we can copy or pass values to another object is by using the spread operator. This feature, that was released in the ES6 version, allows us to create a new object by copying the properties of an existing object. For example, if we wanted to copy the myName object into a new one, this is how we would do it:


const myName = {

name: "Leonardo"

};

const myPerson = {

...myName

}

console.log(myPerson);

// Result -> { name: "Leonardo" }

And if we wanted to copy the properties of myName and add a new property to our new object:


const myName = {

name: "Leonardo"

};

const myPerson = {

...myName,

age: 23

}

console.log(myPerson);

// Result -> { name: "Leonardo", age: 23 }

Redux

The first principle of Redux is immutability, that’s why we should mention Redux here. Not only because it’s the most famous and used state management library for React applications, but also because it has the immutability concept in its core ideas. The right way to use Redux is by having immutable reducers.

Redux didn’t invent the concept of immutability — it’s way older than this state management library — but we must recognize that with this library a lot of developers started to use and talk about immutability.

If you don’t know how Redux works, this is a pretty simplified explanation, just so you can understand why the immutability is important here:

  1. Redux allows you to hold all your data and state in one single object, what we call store. This can help us to achieve a nice level of scalability and maintainability. So let’s imagine that we have our store, and inside that store, we have our initial state:

const initialState = {

name: "Leonardo Maldonado",

age: 22

}

  1. If we want to change our state, we should dispatch an action. An action in Redux is an object with two properties:

  2. type— which describes the type of our action, what exactly this action does.

  3. payload — describes exactly what should change.

So, an action in Redux looks like this:


const changeAge = payload => ({

type: 'CHANGE_AGE',

payload

})

We have our initial state; we created the action that will be dispatched to change the state; now we’ll create our reducer and understand how the immutability concept is used in Redux and why it’s so important to have immutable data.

  1. A reducer is basically a function that reads the type of action that was dispatched, and, based on the action type, it produces the next state and merges the action payload into the new state. In our case, we dispatched an action called CHANGE_AGE, so in our reducer function, we should have a case to deal with when this action is dispatched.

const initialState = {

name: "Leonardo Maldonado"

age: 22

}

const reducer = (state = initialState, action) => {

switch (action.type) {

case 'CHANGE_AGE':

return {

...state,

age: action.payload

}

default:

return state;

}

}

This is where the magic happens: when our CHANGE_AGE action is dispatched, our reducer has to perform a task based on the type of the action. In our case it changes the age, but it also has to maintain the original value of our initial state, in our case the name. It’s pretty important to maintain our initial state. Otherwise, we would lose data very easily and it would be very hard to keep track of our data. That’s why the first principle of Redux its immutability.

Immer

If you’re into React development and are not using Redux right now but want to have an immutable state in your application, you can use the Immer library. Basically, this is how this library works:

Immer in action

  1. You have your current state.

  2. It lets you apply your changes to the draftState, basically a copy of the currentState.

  3. After all your changes are completed, it’ll produce your nextState based on the changes in the draftState.

For example, let’s imagine that we have our current state and we wanted to add a new object to this array. We would use the produce function.


import produce from "immer";

const state = [

{

name: "Leonardo",

age: 23

},

{

name: "Lucas",

age: 20

}

];

const nextState = produce(state, draftState => {

draftState.push({

name: "Carlos",

age: 18

})

});

Basically the produce functions receive two parameters: the currentState, and a callback function, which we’ll use to modify our draftState. This function we’ll produce our nextState. Pretty simple, yet very powerful.

If you’re working with React and are having problems with your state management in your application, I’d really recommend you use this library. It might take some time to understand exactly how this library works, but it’ll save you a lot of time in the future in case your applications grow hugely.

Conclusion

Immutability is not a JavaScript-specific topic — it can be applied in every language — and it’s very recommendable that you use it in any language. The point to pay attention to is how you’re managing your data, and if you’re doing everything that you can to assure that your data is immutable and you’re following a nice pattern of clean code.

In this article, we learned about immutability in JavaScript, what this concept is that has been widely talked about in this past year by functional programming developers, and how it’s being used in a lot of JavaScript applications nowadays. We also learned more about how JavaScript has a lot of immutable methods to add, edit and delete data, and how we can use vanilla JavaScript to have an immutable piece of code. By using immutability in your application, you’ll see only positive points — it’ll improve the way you think about code and make your code cleaner and easier to understand. So, start to write more immutable code now, and see how it’ll help you improve your developer life!


Leonardo Maldonado
About the Author

Leonardo Maldonado

Leonardo is a full-stack developer, working with everything React-related, and loves to write about React and GraphQL to help developers. He also created the 33 JavaScript Concepts.

Related Posts

Comments

Comments are disabled in preview mode.