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;
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;
What’s the improvement?
-
No extra state for filtered products: We no longer store a separate list of
filteredProducts
. Instead, we directly compute the filtered list from theproducts
array andsearchQuery
on every render. - Cleaner code: The component is simpler, with fewer states to manage. This makes it easier to read and maintain.
- 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)
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