Ever wondered why your set state function is not updating the state sometimes when the stored value is a reference type like array or object. Let me tell you why. But first, let's build a foundation so that we can cop up with the concepts that will be explained in this blog.
Principle of Immutability in React:
The principle of immutability in React refers to the practice of not directly mutating state or data structures, but instead creating new copies of them whenever changes are needed. In simpler terms, once a value is created, it should not be changed, but rather replaced with a new value when modifications are required.
Reference Types:
All the data types in JavaScript are divided into two main categories:
- Primitive Types
- Non primitive or Reference Types
Primitive types are the basic building blocks or fundamental types of data. For example, numbers, strings, Boolean, null and undefined etc.
// Primitive types
var name = "Umair Syed"; // String
var age = 25; // number
var isCool = true; // boolean
var favoriteMovie = null; // null
var salary = undefined; // undefined
Reference types are complex types that are made up from other types like Primitive or even other reference types. You can consider them as a combination of multiple primitive types in a single place. For example, an object or array where we can store a group of values including strings, numbers, Booleans and so on. In JavaScript, reference types are Arrays, Objects, and Functions.
Why are they called reference types? Because in JavaScript, when you create a variable and assign it a reference type value, you're not actually storing the entire value in that variable. Instead, you're storing a reference to where the value is stored in memory, compared to Primitive type where value is stored in that variable. This means that if you have two variables pointing to the same reference type value, they both refer to the same data in memory. As a result, changes made to the data through one variable will also be reflected when accessing the data through the other variable.
// Reference types
// These values are stored in a heap and person variable is just storing
// the pointer to that place in memory. You can think of it as
// person = 0x7ffd84520a38
var person = {
name: "Umair Syed",
age: 25,
};
// Since we assigned an object to anotherPerson, that is why a pointer is
// stored in anotherPerson as well. A new pointer? Nope, the same
// pointer as stored in person. For the sake of example:
// anotherPerson = 0x7ffd84520a38;
var anotherPerson = person;
// Since both objects have the same pointer, changing any property in
// anotherPerson will be reflected in person as well
anotherPerson.name = "Faisal Tahir";
console.log(person); // { name: "Faisal Tahir", age: 25 }
console.log(anotherPerson); // { name: "Faisal Tahir", age: 25 }
Keeping the above points in mind, it is very crucial to take care of the type that we are updating in the React state. Now, what is the wrong way of updating a state that has reference value and what is the correct way?
Wrong Way:
Let's say you are showing an array of sorted values in a list, and you want to change the sorting order from Ascending to Descending or vice versa. How you would do it. Most probably, the novice will think of the following way:
function OrderedList() {
const [sortedArr, setSortedArr] = useState([1, 2, 3, 4]);
return (
<div>
<ol>
{sortedArr.map((value, i) => (
<li key={i}>{value}</li>
))}
</ol>
<button
onClick={() => setSortedArr((currentValue) => currentValue.reverse())}
>
Reverse
</button>
</div>
);
}
On clicking the Reverse button, the list will not update, why? Let's understand:
The reverse() method reverses the elements of an array in place, meaning it modifies the original array and returns a reference to the same array with its elements reversed.
Since reverse() modifies the original array, React might not detect the change in the array's content. React compares the new state with the previous state using reference equality and shallow comparison. If the reference to the array remains the same after the update (which is the case when using reverse()), React might not recognize the change and may not trigger a re-render.
Correct Way:
To fix this issue, you should ensure that you create a new array with the reversed elements rather than modifying the original array in place. You can achieve this using the spread operator (...) or other array methods like slice() or concat():
setSortedArray(currentValue => [...currentValue].reverse());
This way, you're creating a new array with the reversed elements, maintaining immutability, and ensuring that React detects the change in state correctly, triggering a re-render as expected.
Conclusion: It's essential to always create a new array or object when updating state that contains a reference type. This ensures adherence to the principle of immutability, which is crucial for React's state management to function correctly. By creating new copies of arrays or objects instead of modifying them directly, you guarantee that React accurately detects changes and triggers re-renders as needed, maintaining the integrity of your application's state.
Top comments (0)