DEV Community

Vivek Kurmi
Vivek Kurmi

Posted on

Immutable Data Patterns in React

The immutable pattern involves treating data as if it were immutable, meaning it cannot be changed directly. Instead of modifying existing data, you create a new copy with the desired changes.
This pattern is particularly relevant when working with React, as it helps prevent unexpected side effects and makes it easier to manage the state of your application.
This approach helps maintain a clear history of state changes and avoids unintended consequences.

Let's illustrate this with an example where we're updating an array of items in a React component.

Step 1: Problem Code - Mutating State Directly

In React, modifying the state directly can lead to unexpected behavior and make it challenging to track changes. This can result in bugs that are hard to identify and fix.

import React, { useState } from 'react';

const MutableList = () => {
  const [items, setItems] = useState(['item1', 'item2', 'item3']);

  const addItem = () => {
    // Directly modifying the state array
    items.push('newItem');
    setItems(items); // Incorrect! Does not trigger a re-render
  };

  return (
    <div>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={addItem}>Add Item</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

In this example, we are directly modifying the items array and then setting the state with the same array. This can lead to issues because React may not recognize the change, as we are mutating the existing array.

Step 2: Solution - Immutable Pattern

Let's use the immutable pattern to update the array by creating a new copy.

import React, { useState } from 'react';

const ImmutableList = () => {
  const [items, setItems] = useState(['item1', 'item2', 'item3']);

  const addItem = () => {
    // Creating a new array with the new item
    const newItems = [...items, 'newItem'];
    setItems(newItems); // Correct! Triggers a re-render
  };

  return (
    <div>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={addItem}>Add Item</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Explanation:

  1. Problem Code:
   const addItem = () => {
     items.push('newItem');
     setItems(items); // Incorrect! Mutating the existing array
   };
Enter fullscreen mode Exit fullscreen mode

In the problem code, we directly modify the items array and then set the state with the same array. This may not trigger a re-render because React might not recognize the change.

  1. Solution Code:
   const addItem = () => {
     const newItems = [...items, 'newItem'];
     setItems(newItems); // Correct! Creating a new array and setting the state
   };
Enter fullscreen mode Exit fullscreen mode

In the solution code, we use the spread operator (...) to create a new array with the existing items and the new item. This ensures that we are not mutating the original array, and React recognizes the state change, triggering a re-render.

By following the immutable pattern, we create a more predictable and maintainable codebase in React. It helps prevent bugs related to state mutations and improves the overall stability of your application.

"Your feedback and ideas are invaluable – drop a comment, and let's make this even better!"

😍 If you enjoy the content, please 👍 like, 🔄 share, and 👣 follow for more updates!
Join me on a professional journey through my LinkedIn profile: Linkedin Profile

Top comments (0)