javascript
54 lines · 8 steps
Memoizing a filtered list in React
A search-and-sort product list that recomputes its visible items only when the inputs that matter actually change.
Explained by
highlit
1import { useMemo, useState } from 'react';
2
3function ProductList({ products }) {
4 const [query, setQuery] = useState('');
5 const [minRating, setMinRating] = useState(0);
6 const [sortBy, setSortBy] = useState('relevance');
7
8 const visibleProducts = useMemo(() => {
9 const term = query.trim().toLowerCase();
10
11 const filtered = products.filter((product) => {
12 if (product.rating < minRating) return false;
13 if (!term) return true;
14 return (
15 product.name.toLowerCase().includes(term) ||
16 product.tags.some((tag) => tag.toLowerCase().includes(term))
17 );
18 });
19
20 if (sortBy === 'price') {
21 filtered.sort((a, b) => a.price - b.price);
22 } else if (sortBy === 'rating') {
23 filtered.sort((a, b) => b.rating - a.rating);
24 }
25
26 return filtered;
27 }, [products, query, minRating, sortBy]);
28
29 return (
30 <div className="product-list">
31 <input
32 type="search"
33 value={query}
34 placeholder="Search products"
35 onChange={(e) => setQuery(e.target.value)}
36 />
37 <select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
38 <option value="relevance">Relevance</option>
39 <option value="price">Price</option>
40 <option value="rating">Rating</option>
41 </select>
42 <p>{visibleProducts.length} results</p>
43 <ul>
44 {visibleProducts.map((product) => (
45 <li key={product.id}>
46 {product.name} — ${product.price.toFixed(2)}
47 </li>
48 ))}
49 </ul>
50 </div>
51 );
52}
53
54export default ProductList;
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Derive displayed data from state inside render rather than storing a second copy you have to keep in sync.
- 2Wrap expensive list transforms in useMemo with an honest dependency array so they rerun only when an input changes.
- 3Controlled inputs feed state, state feeds the memo, and the memo feeds the UI in one predictable flow.
Related explainers
ruby
require "csv" class SalesReport def initialize(path)
Aggregating CSV sales data in Ruby
data-aggregation
memoization
group_by
Intermediate
6 steps
javascript
'use server' import { revalidatePath } from 'next/cache' import { redirect } from 'next/navigation'
How a Next.js Server Action updates a post
server-actions
authorization
validation
Intermediate
7 steps
javascript
const express = require('express'); const v1 = express.Router();
Versioning an API with Express Routers
api versioning
routing
modularity
Intermediate
10 steps
javascript
const RATE_LIMIT = 100; const WINDOW_MS = 60 * 1000; const BLOCK_MS = 5 * 60 * 1000;
Building a rate-limiting middleware in Express
rate-limiting
middleware
closures
Intermediate
9 steps
rust
use std::collections::HashMap; pub struct Memoizer<K, V, F> { cache: HashMap<K, V>,
A generic memoizer in Rust
memoization
generics
caching
Intermediate
6 steps
javascript
const transitions = { cart: { checkout: 'shipping' }, shipping: { submitAddress: 'payment', back: 'cart' }, payment: { submitPayment: 'review', back: 'shipping' },
A finite state machine for checkout flow
state-machine
event-driven
data-driven-design
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/memoizing-a-filtered-list-in-react-explained-javascript-f6cb/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.