DEV Community

Cover image for React, we need to talk about Derived States!
Bruno Henrique
Bruno Henrique

Posted on

React, we need to talk about Derived States!

When working with React, you’ll often encounter situations where you need to transform or derive values based on other state or props. This concept is known as derived state, and it's one of the most powerful tools in your React toolkit—if used correctly.

“Derived State: State that can be computed from existing piece of state or prop is called derived state.”

So, let’s dive into it and have a bit of fun while we’re at it.


The Bad Practice: Filtering the Product List

Let’s start with a typical "oops, I didn’t think this through" example. Imagine we have a list of products, and we want to filter them based on what the user types into a search input. In an ideal world, our search should update dynamically and be super snappy. But, let’s take a quick look at the bad practice approach first.

Here’s how we shouldn’t be handling things:

import React, { useState } from 'react';

const ProductList = () => {
  const [searchQuery, setSearchQuery] = useState('');
  const [products, setProducts] = useState([
    { id: 1, name: 'Laptop' },
    { id: 2, name: 'Smartphone' },
    { id: 3, name: 'Headphones' },
    { id: 4, name: 'Smartwatch' },
  ]);
  const [filteredProducts, setFilteredProducts] = useState(products);

  const handleSearch = (e) => {
    const query = e.target.value;
    setSearchQuery(query);
    setFilteredProducts(
      products.filter(product =>
        product.name.toLowerCase().includes(query.toLowerCase())
      )
    );
  };

  return (
    <div>
      <input
        type="text"
        value={searchQuery}
        onChange={handleSearch}
        placeholder="Search for a product"
      />
      <ul>
        {filteredProducts.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default ProductList;
Enter fullscreen mode Exit fullscreen mode

Why is this bad practice?

It looks like it works, right? The search input is hooked up, and it filters the products as expected.

But here’s the problem: we’re storing the filtered products as a separate state. This causes unnecessary duplication of data. We already have products in state, and we’re storing the result of the filter operation in another state, which leads to potential bugs, increased memory usage, and makes it harder to keep everything in sync.

Basically, we’re making things more complicated than they need to be.


The Good Practice: Using Derived State

Now, let's apply a bit of React wisdom and fix the above code using derived state.

This time, instead of keeping two separate state variables (products and filteredProducts), we'll derive the filtered products directly from the products array based on the searchQuery. This way, we avoid redundancy and keep our state clean.

Here’s the improved version:

import React, { useState } from 'react';

const ProductList = () => {
  const [searchQuery, setSearchQuery] = useState('');
  const products = [
    { id: 1, name: 'Laptop' },
    { id: 2, name: 'Smartphone' },
    { id: 3, name: 'Headphones' },
    { id: 4, name: 'Smartwatch' },
  ];

  // Derived state: filter products based on the search query
  const filteredProducts = products.filter(product =>
    product.name.toLowerCase().includes(searchQuery.toLowerCase())
  );

  const handleSearch = (e) => {
    setSearchQuery(e.target.value);
  };

  return (
    <div>
      <input
        type="text"
        value={searchQuery}
        onChange={handleSearch}
        placeholder="Search for a product"
      />
      <ul>
        {filteredProducts.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default ProductList;
Enter fullscreen mode Exit fullscreen mode

What’s the improvement?

  1. No extra state for filtered products: We no longer store a separate list of filteredProducts. Instead, we directly compute the filtered list from the products array and searchQuery on every render.
  2. Cleaner code: The component is simpler, with fewer states to manage. This makes it easier to read and maintain.
  3. Performance boost (sort of): React doesn’t need to track extra state variables. It just derives the filtered list directly from the existing data, making the code leaner and faster (even though React optimizes updates, fewer state changes are always better).

This approach takes advantage of derived state, where the filtered products are computed from existing data, instead of storing an additional copy of the filtered data.


When Not to Use Derived State

While derived state is often the best choice, it’s not the silver bullet for everything. If your component is dealing with complex calculations or state that’s needed across multiple parts of the application, it might be better to use memoization or store that derived state in a higher-level component or context.

But for simple filtering, sorting, or other lightweight derived values, the example above should be your go-to approach.


Conclusion

To sum up, derived state in React is all about keeping things DRY—don’t repeat yourself. Instead of keeping unnecessary copies of the same data in state, calculate values on the fly based on existing state and props. This leads to cleaner, more efficient code that’s easier to maintain. So, the next time you’re tempted to duplicate data in React, think about whether you can calculate it directly from other sources. Your future self will thank you!


Top comments (1)

Collapse
 
pengeszikra profile image
Peter Vivo

This solution smell, id I not make a mistake the filteredProduct computing is happen every rendering time.
I think a best practice is when the filtering happen on server side