The
useDeferredValue hook complements useTransition, allowing you to "defer" a value so that React can treat updates as less urgent. It is particularly useful when you want to keep the UI responsive while rendering heavy components.1import { useDeferredValue, useState } from 'react';
2
3function SearchApp() {
4 const [query, setQuery] = useState('');
5 const deferredQuery = useDeferredValue(query);
6
7 return (
8 <div>
9 <input
10 value={query}
11 onChange={(e) => setQuery(e.target.value)}
12 />
13
14 {/* Input updates immediately */}
15 <p>Current query: {query}</p>
16
17 {/* List uses the deferred value */}
18 <SearchResults query={deferredQuery} />
19 </div>
20 );
21}
22
23function SearchResults({ query }) {
24 // Heavy computations with deferred value
25 const results = performHeavySearch(query);
26
27 return (
28 <div>
29 <p>Results for: "{query}"</p>
30 <ul>
31 {results.map(result => (
32 <li key={result.id}>{result.title}</li>
33 ))}
34 </ul>
35 </div>
36 );
37}
38
39function performHeavySearch(query) {
40 // Simulation of heavy computations
41 const items = [];
42 for (let i = 0; i < 5000; i++) {
43 if (`Item ${i}`.toLowerCase().includes(query.toLowerCase())) {
44 items.push({ id: i, title: `Item ${i}` });
45 }
46 }
47 return items;
48}1import { useDeferredValue, useTransition, useState } from 'react';
2
3// Approach 1: useDeferredValue
4function DeferredValueExample() {
5 const [input, setInput] = useState('');
6 const deferredInput = useDeferredValue(input);
7
8 return (
9 <div>
10 <h3>useDeferredValue</h3>
11 <input
12 value={input}
13 onChange={(e) => setInput(e.target.value)}
14 />
15 <HeavyComponent text={deferredInput} />
16 </div>
17 );
18}
19
20// Approach 2: useTransition
21function TransitionExample() {
22 const [input, setInput] = useState('');
23 const [deferredInput, setDeferredInput] = useState('');
24 const [isPending, startTransition] = useTransition();
25
26 const handleChange = (value) => {
27 setInput(value); // Immediate update
28 startTransition(() => {
29 setDeferredInput(value); // Deferred update
30 });
31 };
32
33 return (
34 <div>
35 <h3>useTransition</h3>
36 <input
37 value={input}
38 onChange={(e) => handleChange(e.target.value)}
39 />
40 {isPending && <div>Updating...</div>}
41 <HeavyComponent text={deferredInput} />
42 </div>
43 );
44}
45
46function HeavyComponent({ text }) {
47 // Simulation of heavy rendering
48 const items = [];
49 for (let i = 0; i < 1000; i++) {
50 items.push(`${text} - element ${i}`);
51 }
52
53 return (
54 <div style={{ height: '200px', overflow: 'auto' }}>
55 {items.map((item, index) => (
56 <div key={index} style={{ padding: '2px' }}>
57 {item}
58 </div>
59 ))}
60 </div>
61 );
62}
63
64// Comparison of both approaches
65function ComparisonDemo() {
66 return (
67 <div style={{ display: 'flex', gap: '20px' }}>
68 <div style={{ flex: 1 }}>
69 <DeferredValueExample />
70 </div>
71 <div style={{ flex: 1 }}>
72 <TransitionExample />
73 </div>
74 </div>
75 );
76}1import { useDeferredValue, useState, memo } from 'react';
2
3// Component optimized with memo
4const OptimizedList = memo(function OptimizedList({ items, highlightTerm }) {
5 console.log('OptimizedList renders for:', highlightTerm);
6
7 return (
8 <div style={{ height: '300px', overflow: 'auto', border: '1px solid #ddd' }}>
9 {items.map(item => (
10 <div
11 key={item.id}
12 style={{
13 padding: '8px',
14 borderBottom: '1px solid #eee',
15 backgroundColor: item.id % 2 === 0 ? '#f9f9f9' : '#fff'
16 }}
17 >
18 <strong>
19 {highlightText(item.title, highlightTerm)}
20 </strong>
21 <div style={{ fontSize: '14px', color: '#666' }}>
22 {highlightText(item.description, highlightTerm)}
23 </div>
24 </div>
25 ))}
26 </div>
27 );
28});
29
30function SearchWithOptimization() {
31 const [searchTerm, setSearchTerm] = useState('');
32 const deferredSearchTerm = useDeferredValue(searchTerm);
33
34 // Generating data
35 const allItems = generateSearchData(2000);
36
37 // Filtering with deferred value
38 const filteredItems = allItems.filter(item =>
39 item.title.toLowerCase().includes(deferredSearchTerm.toLowerCase()) ||
40 item.description.toLowerCase().includes(deferredSearchTerm.toLowerCase())
41 );
42
43 const isStale = searchTerm !== deferredSearchTerm;
44
45 return (
46 <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
47 <h2>Search with useDeferredValue</h2>
48
49 <input
50 value={searchTerm}
51 onChange={(e) => setSearchTerm(e.target.value)}
52 style={{
53 width: '100%',
54 padding: '12px',
55 fontSize: '16px',
56 border: '2px solid #ddd',
57 borderRadius: '8px',
58 marginBottom: '10px'
59 }}
60 />
61
62 <div style={{
63 display: 'flex',
64 justifyContent: 'space-between',
65 alignItems: 'center',
66 marginBottom: '10px',
67 fontSize: '14px'
68 }}>
69 <span>
70 Found {filteredItems.length} results
71 {deferredSearchTerm && ` for "${deferredSearchTerm}"`}
72 </span>
73
74 {isStale && (
75 <span style={{ color: '#007bff', fontStyle: 'italic' }}>
76 Updating...
77 </span>
78 )}
79 </div>
80
81 <div style={{ opacity: isStale ? 0.7 : 1, transition: 'opacity 0.2s' }}>
82 <OptimizedList
83 items={filteredItems}
84 highlightTerm={deferredSearchTerm}
85 />
86 </div>
87 </div>
88 );
89}
90
91function generateSearchData(count) {
92 const categories = ['Electronics', 'Books', 'Clothing', 'Home', 'Sports'];
93 const adjectives = ['Modern', 'Classic', 'Premium', 'Budget', 'Universal'];
94
95 return Array.from({ length: count }, (_, i) => ({
96 id: i,
97 title: `${adjectives[i % adjectives.length]} Product ${i + 1}`,
98 description: `Product description from the ${categories[i % categories.length]} category. High-quality item.`,
99 category: categories[i % categories.length],
100 price: Math.floor(Math.random() * 1000) + 50
101 }));
102}
103
104function highlightText(text, term) {
105 if (!term) return text;
106
107 const regex = new RegExp(`(${term})`, 'gi');
108 const parts = text.split(regex);
109
110 return parts.map((part, index) =>
111 part.toLowerCase() === term.toLowerCase() ? (
112 <mark key={index} style={{ backgroundColor: '#ffeb3b', padding: '2px' }}>
113 {part}
114 </mark>
115 ) : part
116 );
117}1import { useDeferredValue, useState, Suspense } from 'react';
2
3function AdaptiveLoadingDemo() {
4 const [selectedTab, setSelectedTab] = useState('popular');
5 const deferredTab = useDeferredValue(selectedTab);
6
7 const tabs = [
8 { id: 'popular', label: 'Popular', icon: '🔥' },
9 { id: 'newest', label: 'Newest', icon: '🆕' },
10 { id: 'trending', label: 'Trending', icon: '📈' }
11 ];
12
13 return (
14 <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
15 <h2>Adaptive Loading with useDeferredValue</h2>
16
17 {/* Tab Navigation */}
18 <div style={{
19 display: 'flex',
20 marginBottom: '20px',
21 borderBottom: '1px solid #ddd'
22 }}>
23 {tabs.map(tab => (
24 <button
25 key={tab.id}
26 onClick={() => setSelectedTab(tab.id)}
27 style={{
28 padding: '12px 20px',
29 border: 'none',
30 backgroundColor: selectedTab === tab.id ? '#007bff' : 'transparent',
31 color: selectedTab === tab.id ? 'white' : '#007bff',
32 cursor: 'pointer',
33 borderRadius: '8px 8px 0 0',
34 marginRight: '5px',
35 fontSize: '16px'
36 }}
37 >
38 {tab.icon} {tab.label}
39 </button>
40 ))}
41 </div>
42
43 {/* Content with deferred loading */}
44 <div style={{ position: 'relative' }}>
45 {/* Show loading state when tab is changing */}
46 {selectedTab !== deferredTab && (
47 <div style={{
48 position: 'absolute',
49 top: 0,
50 left: 0,
51 right: 0,
52 bottom: 0,
53 backgroundColor: 'rgba(255, 255, 255, 0.8)',
54 display: 'flex',
55 alignItems: 'center',
56 justifyContent: 'center',
57 zIndex: 1,
58 borderRadius: '8px'
59 }}>
60 <div style={{
61 padding: '20px',
62 backgroundColor: 'white',
63 borderRadius: '8px',
64 boxShadow: '0 2px 10px rgba(0,0,0,0.1)'
65 }}>
66 Loading content...
67 </div>
68 </div>
69 )}
70
71 <Suspense fallback={<TabContentSkeleton />}>
72 <TabContent tab={deferredTab} />
73 </Suspense>
74 </div>
75 </div>
76 );
77}
78
79function TabContent({ tab }) {
80 // Simulation of heavy data loading
81 const content = generateTabContent(tab);
82
83 return (
84 <div style={{
85 minHeight: '400px',
86 padding: '20px',
87 backgroundColor: '#f8f9fa',
88 borderRadius: '8px'
89 }}>
90 <h3>Tab content: {tab}</h3>
91 <div style={{ display: 'grid', gap: '15px' }}>
92 {content.map(item => (
93 <div
94 key={item.id}
95 style={{
96 padding: '15px',
97 backgroundColor: 'white',
98 borderRadius: '6px',
99 boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
100 }}
101 >
102 <h4 style={{ margin: '0 0 8px 0', color: '#333' }}>
103 {item.title}
104 </h4>
105 <p style={{ margin: 0, color: '#666', fontSize: '14px' }}>
106 {item.description}
107 </p>
108 <div style={{
109 marginTop: '8px',
110 fontSize: '12px',
111 color: '#999'
112 }}>
113 {item.date} • {item.views} views
114 </div>
115 </div>
116 ))}
117 </div>
118 </div>
119 );
120}
121
122function TabContentSkeleton() {
123 return (
124 <div style={{
125 minHeight: '400px',
126 padding: '20px',
127 backgroundColor: '#f8f9fa',
128 borderRadius: '8px'
129 }}>
130 <div style={{
131 height: '24px',
132 backgroundColor: '#e9ecef',
133 borderRadius: '4px',
134 marginBottom: '20px',
135 width: '200px'
136 }} />
137
138 {Array.from({ length: 5 }).map((_, i) => (
139 <div
140 key={i}
141 style={{
142 padding: '15px',
143 backgroundColor: 'white',
144 borderRadius: '6px',
145 marginBottom: '15px',
146 boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
147 }}
148 >
149 <div style={{
150 height: '18px',
151 backgroundColor: '#e9ecef',
152 borderRadius: '4px',
153 marginBottom: '8px',
154 width: '70%'
155 }} />
156 <div style={{
157 height: '14px',
158 backgroundColor: '#f1f3f4',
159 borderRadius: '4px',
160 marginBottom: '4px'
161 }} />
162 <div style={{
163 height: '14px',
164 backgroundColor: '#f1f3f4',
165 borderRadius: '4px',
166 width: '60%'
167 }} />
168 </div>
169 ))}
170 </div>
171 );
172}
173
174function generateTabContent(tab) {
175 const contentMap = {
176 popular: [
177 { id: 1, title: 'Most popular article', description: 'This article was the most popular this week.', date: '2024-01-15', views: 15420 },
178 { id: 2, title: 'Second in ranking', description: 'Great material that earned reader recognition.', date: '2024-01-14', views: 12380 },
179 { id: 3, title: 'Top 3 popularity', description: 'Also a very interesting article with a high rating.', date: '2024-01-13', views: 9560 }
180 ],
181 newest: [
182 { id: 4, title: 'Latest post', description: 'Freshly published blog material.', date: '2024-01-16', views: 245 },
183 { id: 5, title: 'Yesterday\'s article', description: 'Published yesterday, already gaining popularity.', date: '2024-01-15', views: 1230 },
184 { id: 6, title: 'From 2 days ago', description: 'Still relevant and valuable material.', date: '2024-01-14', views: 2100 }
185 ],
186 trending: [
187 { id: 7, title: 'Viral on social media', description: 'This article is taking social media by storm!', date: '2024-01-12', views: 25600 },
188 { id: 8, title: 'Rising popularity', description: 'Quickly gaining popularity.', date: '2024-01-11', views: 8970 },
189 { id: 9, title: 'Trend of the week', description: 'The main trend of this week in our industry.', date: '2024-01-10', views: 14520 }
190 ]
191 };
192
193 return contentMap[tab] || [];
194}1import { useDeferredValue, useState, useEffect, useMemo } from 'react';
2
3function SmartFilteringDemo() {
4 const [searchQuery, setSearchQuery] = useState('');
5 const [priceRange, setPriceRange] = useState([0, 1000]);
6 const [category, setCategory] = useState('all');
7
8 // Deferred values for heavy operations
9 const deferredQuery = useDeferredValue(searchQuery);
10 const deferredPriceRange = useDeferredValue(priceRange);
11 const deferredCategory = useDeferredValue(category);
12
13 // Check if filtering is in progress
14 const isFiltering =
15 searchQuery !== deferredQuery ||
16 priceRange !== deferredPriceRange ||
17 category !== deferredCategory;
18
19 const products = useMemo(() => generateProducts(1000), []);
20
21 // Filtering with deferred values
22 const filteredProducts = useMemo(() => {
23 return products.filter(product => {
24 const matchesSearch = product.name.toLowerCase().includes(deferredQuery.toLowerCase()) ||
25 product.description.toLowerCase().includes(deferredQuery.toLowerCase());
26
27 const matchesPrice = product.price >= deferredPriceRange[0] &&
28 product.price <= deferredPriceRange[1];
29
30 const matchesCategory = deferredCategory === 'all' ||
31 product.category === deferredCategory;
32
33 return matchesSearch && matchesPrice && matchesCategory;
34 });
35 }, [products, deferredQuery, deferredPriceRange, deferredCategory]);
36
37 return (
38 <div style={{ maxWidth: '1200px', margin: '0 auto', padding: '20px' }}>
39 <h2>Smart Filtering with useDeferredValue</h2>
40
41 <div style={{
42 display: 'grid',
43 gridTemplateColumns: '300px 1fr',
44 gap: '20px'
45 }}>
46 {/* Filters Sidebar */}
47 <div style={{
48 padding: '20px',
49 backgroundColor: '#f8f9fa',
50 borderRadius: '8px',
51 height: 'fit-content'
52 }}>
53 <h3 style={{ marginTop: 0 }}>Filters</h3>
54
55 {/* Search Filter */}
56 <div style={{ marginBottom: '20px' }}>
57 <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
58 Search:
59 </label>
60 <input
61 value={searchQuery}
62 onChange={(e) => setSearchQuery(e.target.value)}
63 style={{
64 width: '100%',
65 padding: '8px',
66 border: '1px solid #ddd',
67 borderRadius: '4px'
68 }}
69 />
70 </div>
71
72 {/* Category Filter */}
73 <div style={{ marginBottom: '20px' }}>
74 <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
75 Category:
76 </label>
77 <select
78 value={category}
79 onChange={(e) => setCategory(e.target.value)}
80 style={{
81 width: '100%',
82 padding: '8px',
83 border: '1px solid #ddd',
84 borderRadius: '4px'
85 }}
86 >
87 <option value="all">All</option>
88 <option value="Electronics">Electronics</option>
89 <option value="Books">Books</option>
90 <option value="Clothing">Clothing</option>
91 <option value="Home">Home</option>
92 </select>
93 </div>
94
95 {/* Price Range Filter */}
96 <div style={{ marginBottom: '20px' }}>
97 <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
98 Price: {priceRange[0]} - {priceRange[1]} $
99 </label>
100 <input
101 type="range"
102 min="0"
103 max="1000"
104 value={priceRange[1]}
105 onChange={(e) => setPriceRange([priceRange[0], parseInt(e.target.value)])}
106 style={{ width: '100%' }}
107 />
108 </div>
109
110 {/* Filter Status */}
111 <div style={{
112 padding: '10px',
113 backgroundColor: isFiltering ? '#fff3cd' : '#d4edda',
114 borderRadius: '4px',
115 fontSize: '14px',
116 textAlign: 'center'
117 }}>
118 {isFiltering ? (
119 <span>Filtering...</span>
120 ) : (
121 <span>Done</span>
122 )}
123 </div>
124 </div>
125
126 {/* Results */}
127 <div>
128 <div style={{
129 display: 'flex',
130 justifyContent: 'space-between',
131 alignItems: 'center',
132 marginBottom: '20px'
133 }}>
134 <h3 style={{ margin: 0 }}>
135 Products ({filteredProducts.length})
136 </h3>
137
138 {isFiltering && (
139 <span style={{ color: '#007bff', fontSize: '14px' }}>
140 Updating results...
141 </span>
142 )}
143 </div>
144
145 <div style={{
146 opacity: isFiltering ? 0.7 : 1,
147 transition: 'opacity 0.3s',
148 display: 'grid',
149 gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
150 gap: '15px'
151 }}>
152 {filteredProducts.slice(0, 20).map(product => (
153 <div
154 key={product.id}
155 style={{
156 padding: '15px',
157 backgroundColor: 'white',
158 border: '1px solid #e9ecef',
159 borderRadius: '8px',
160 boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
161 }}
162 >
163 <h4 style={{ margin: '0 0 8px 0', fontSize: '16px' }}>
164 {highlightText(product.name, deferredQuery)}
165 </h4>
166 <p style={{
167 margin: '0 0 8px 0',
168 fontSize: '14px',
169 color: '#666',
170 lineHeight: '1.4'
171 }}>
172 {highlightText(product.description, deferredQuery)}
173 </p>
174 <div style={{
175 display: 'flex',
176 justifyContent: 'space-between',
177 alignItems: 'center',
178 fontSize: '14px'
179 }}>
180 <span style={{
181 backgroundColor: '#e9ecef',
182 padding: '2px 6px',
183 borderRadius: '4px',
184 fontSize: '12px'
185 }}>
186 {product.category}
187 </span>
188 <span style={{
189 fontWeight: 'bold',
190 color: '#007bff',
191 fontSize: '16px'
192 }}>
193 ${product.price}
194 </span>
195 </div>
196 </div>
197 ))}
198 </div>
199
200 {filteredProducts.length === 0 && (
201 <div style={{
202 textAlign: 'center',
203 padding: '40px',
204 color: '#666'
205 }}>
206 No products match the criteria
207 </div>
208 )}
209 </div>
210 </div>
211 </div>
212 );
213}
214
215function generateProducts(count) {
216 const categories = ['Electronics', 'Books', 'Clothing', 'Home'];
217 const adjectives = ['Premium', 'Classic', 'Modern', 'Vintage', 'Professional'];
218 const nouns = ['Device', 'Tool', 'Item', 'Product', 'Gadget'];
219
220 return Array.from({ length: count }, (_, i) => ({
221 id: i,
222 name: `${adjectives[i % adjectives.length]} ${nouns[i % nouns.length]} ${i + 1}`,
223 description: `High-quality ${categories[i % categories.length].toLowerCase()} item with excellent features and durability.`,
224 category: categories[i % categories.length],
225 price: Math.floor(Math.random() * 950) + 50
226 }));
227}
228
229function highlightText(text, term) {
230 if (!term) return text;
231
232 const regex = new RegExp(`(${term})`, 'gi');
233 const parts = text.split(regex);
234
235 return parts.map((part, index) =>
236 part.toLowerCase() === term.toLowerCase() ? (
237 <mark key={index} style={{ backgroundColor: '#ffeb3b', padding: '1px' }}>
238 {part}
239 </mark>
240 ) : part
241 );
242}1// GOOD - Heavy lists and filtering
2function ProductSearch() {
3 const [query, setQuery] = useState('');
4 const deferredQuery = useDeferredValue(query);
5
6 return (
7 <>
8 <input value={query} onChange={e => setQuery(e.target.value)} />
9 <HeavyProductList searchTerm={deferredQuery} />
10 </>
11 );
12}
13
14// GOOD - Rendering charts
15function ChartDashboard() {
16 const [dateRange, setDateRange] = useState([startDate, endDate]);
17 const deferredDateRange = useDeferredValue(dateRange);
18
19 return (
20 <>
21 <DateRangePicker onChange={setDateRange} />
22 <ExpensiveChart dateRange={deferredDateRange} />
23 </>
24 );
25}
26
27// BAD - Simple operations
28function SimpleCounter() {
29 const [count, setCount] = useState(0);
30 const deferredCount = useDeferredValue(count); // Unnecessary!
31
32 return <div>{deferredCount}</div>;
33}1import { useDeferredValue, useState, useMemo, useCallback } from 'react';
2
3function OptimizedDataTable() {
4 const [searchTerm, setSearchTerm] = useState('');
5 const [sortConfig, setSortConfig] = useState({ field: 'name', direction: 'asc' });
6
7 const deferredSearchTerm = useDeferredValue(searchTerm);
8 const deferredSortConfig = useDeferredValue(sortConfig);
9
10 const data = useMemo(() => generateLargeDataset(5000), []);
11
12 // Memoized filtering and sorting
13 const processedData = useMemo(() => {
14 let filtered = data;
15
16 if (deferredSearchTerm) {
17 filtered = data.filter(item =>
18 item.name.toLowerCase().includes(deferredSearchTerm.toLowerCase())
19 );
20 }
21
22 return filtered.sort((a, b) => {
23 const direction = deferredSortConfig.direction === 'asc' ? 1 : -1;
24 return a[deferredSortConfig.field].localeCompare(b[deferredSortConfig.field]) * direction;
25 });
26 }, [data, deferredSearchTerm, deferredSortConfig]);
27
28 const handleSort = useCallback((field) => {
29 setSortConfig(prev => ({
30 field,
31 direction: prev.field === field && prev.direction === 'asc' ? 'desc' : 'asc'
32 }));
33 }, []);
34
35 const isStale = searchTerm !== deferredSearchTerm || sortConfig !== deferredSortConfig;
36
37 return (
38 <div>
39 <input
40 value={searchTerm}
41 onChange={(e) => setSearchTerm(e.target.value)}
42 />
43
44 <table style={{ opacity: isStale ? 0.7 : 1 }}>
45 <thead>
46 <tr>
47 <th onClick={() => handleSort('name')}>Name</th>
48 <th onClick={() => handleSort('category')}>Category</th>
49 <th onClick={() => handleSort('price')}>Price</th>
50 </tr>
51 </thead>
52 <tbody>
53 {processedData.slice(0, 50).map(item => (
54 <tr key={item.id}>
55 <td>{item.name}</td>
56 <td>{item.category}</td>
57 <td>{item.price}</td>
58 </tr>
59 ))}
60 </tbody>
61 </table>
62 </div>
63 );
64}1// BAD - Overuse
2function OverusedDeferred() {
3 const [name, setName] = useState('');
4 const [age, setAge] = useState(0);
5 const [email, setEmail] = useState('');
6
7 // Not every value needs deferred!
8 const deferredName = useDeferredValue(name);
9 const deferredAge = useDeferredValue(age);
10 const deferredEmail = useDeferredValue(email);
11
12 return <SimpleForm name={deferredName} age={deferredAge} email={deferredEmail} />;
13}
14
15// BETTER - Selective use
16function BetterApproach() {
17 const [formData, setFormData] = useState({ name: '', age: 0, email: '' });
18 const deferredFormData = useDeferredValue(formData);
19
20 return <HeavyValidationForm data={deferredFormData} />;
21}useDeferredValue is an elegant way to improve the performance of React applications without complex state management. It allows you to keep critical UI elements responsive while performing heavy operations in the background.