Optimizing data flow with the keep-unchanged-values library
Tags: javascript, react, opensource, performance
Published: 2023-12-01 08:19:11

Hello everyone! If you're dealing with frequent data updates in your frontend development projects, especially from sources like backend servers or `localStorage`, this article is essential for you. Discover how the `keep-unchanged-values` library can enhance your React applications by optimizing data updates and minimizing performance issues. You need this if you're navigating through regular updates from various data sources.

**Problem:** Frequent data updates in large objects and lists cause performance issues

**Solution:** `keep-unchanged-values` combined with `React.memo`

**How it works:**

Pass two entities to the `keepUnchangedValues` function you want to "merge". The result is the second entity, and it will keep references to unchanged objects from the first entity:

```js
const oldArray = [1, { a: 1 }, 3];
const newArray = [2, 3, 4, { a: 1 }];

 // result is equal to [2, 3, 4, { a: 1 }]
const result = keepUnchangedValues(oldArray, newArray);

// logs true; result[3] will be a reference to oldArray[1] since they have the same link to the object
console.log(result[3] === oldArray[1]) 
```

**Example:**

1. Update data from external storage periodically 

2. "Merge" new data with existing using `keepUnchangedValues`

3. Update your state with "merged" data: the old nested objects will be the same, new data will be applied

```jsx
import React, { useState, useEffect } from 'react';
import { keepUnchangedValues } from 'keep-unchanged-values';

const MyListComponent = () => {
  const [list, setList] = useState([]);

  useEffect(() => {
    // Fetch data from API
    const fetchHandler = () => {
      fetchData().then(newItems => {
        // Update the list with new items, maintaining unchanged ones
        const updatedList = keepUnchangedValues(list, newItems);
        // if nothing changed, returns the same list
        setList(updatedList);
      });
    }
    // Fetch initial data
    fetchHandler()
    // Set up polling
    const intervalId = setInterval(fetchHandler, 5000); // Polling every 5 seconds

    return () => clearInterval(intervalId);
  }, [list]);

  return (
    <ul>
      {list.map(item => <MemoizedListItem key={item.id} item={item} />)}
    </ul>
  );
};

const MemoizedListItem = React.memo(({ item }) => {
  // Rerenders only if the current item changes.
  // Works with shallow comparison because keepUnchangedValues maintains unchanged values
  useEffect(() => {
    somethingIneficient(item);
  }, [item]);

  return <li>{item.content}</li>;
});

export default MyListComponent;
```

**Benefits:**

- Unchanged list items retain their references

- `React.memo` performs shallow comparisons and skips re-renders for unchanged items

Result? Smooth, efficient data updates!

**Github:** https://github.com/CascaSpace/keep-unchanged-values
**Package:** https://www.npmjs.com/package/keep-unchanged-values

More examples can be seen in the library tests https://github.com/CascaSpace/keep-unchanged-values/blob/main/src/index.test.js

If you want to learn more, check this article where we show how we optimized our Chrome Extension https://cascaspace.substack.com/p/optimizing-performance-how-our-extension