Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

useDeferredValue - opóźnione wartości dla wydajności

Hook

useDeferredValue
to uzupełnienie
useTransition
, które pozwala na "opóźnienie" wartości, dzięki czemu React może traktować aktualizacje jako mniej pilne. Jest szczególnie przydatny, gdy chcemy zachować responsywność UI podczas renderowania ciężkich komponentów.

Podstawy useDeferredValue

1. Składnia i podstawowe użycie

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 aktualizuje się natychmiast */}
15      <p>Aktualne zapytanie: {query}</p>
16      
17      {/* Lista używa opóźnionej wartości */}
18      <SearchResults query={deferredQuery} />
19    </div>
20  );
21}
22
23function SearchResults({ query }) {
24  // Ciężkie obliczenia z opóźnioną wartością
25  const results = performHeavySearch(query);
26  
27  return (
28    <div>
29      <p>Wyniki dla: "{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  // Symulacja ciężkich obliczeń
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}

2. Różnica między useDeferredValue a useTransition

1import { useDeferredValue, useTransition, useState } from 'react';
2
3// Podejście 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// Podejście 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); // Natychmiastowa aktualizacja
28    startTransition(() => {
29      setDeferredInput(value); // Opóźniona aktualizacja
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>Aktualizowanie...</div>}
41      <HeavyComponent text={deferredInput} />
42    </div>
43  );
44}
45
46function HeavyComponent({ text }) {
47  // Symulacja ciężkiego renderowania
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// Porównanie obu podejść
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}

3. Współpraca z memo dla optymalizacji

1import { useDeferredValue, useState, memo } from 'react';
2
3// Komponent zoptymalizowany z memo
4const OptimizedList = memo(function OptimizedList({ items, highlightTerm }) {
5  console.log('OptimizedList renderuje się dla:', 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  // Generowanie danych
35  const allItems = generateSearchData(2000);
36  
37  // Filtrowanie z opóźnioną wartością
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>Wyszukiwarka z 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          Znaleziono {filteredItems.length} wyników
71          {deferredSearchTerm && ` dla "${deferredSearchTerm}"`}
72        </span>
73        
74        {isStale && (
75          <span style={{ color: '#007bff', fontStyle: 'italic' }}>
76            🔄 Aktualizowanie...
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 = ['Elektronika', 'Książki', 'Odzież', 'Dom', 'Sport'];
93  const adjectives = ['Nowoczesny', 'Klasyczny', 'Premium', 'Ekonomiczny', 'Uniwersalny'];
94  
95  return Array.from({ length: count }, (_, i) => ({
96    id: i,
97    title: `${adjectives[i % adjectives.length]} Produkt ${i + 1}`,
98    description: `Opis produktu z kategorii ${categories[i % categories.length]}. Wysokiej jakości artykuł.`,
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}

Zaawansowane wzorce z useDeferredValue

1. Adaptive loading z fallback

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: 'Popularne', icon: '🔥' },
9    { id: 'newest', label: 'Najnowsze', 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 z 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              🔄 Ładowanie zawartości...
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  // Symulacja ciężkiego ładowania danych
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>Zawartość zakładki: {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} wyświetleń
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: 'Najpopularniejszy artykuł', description: 'Ten artykuł cieszył się największą popularnością w tym tygodniu.', date: '2024-01-15', views: 15420 },
178      { id: 2, title: 'Drugi w rankingu', description: 'Świetny materiał, który zdobył uznanie czytelników.', date: '2024-01-14', views: 12380 },
179      { id: 3, title: 'Top 3 popularności', description: 'Również bardzo ciekawy artykuł z wysoką oceną.', date: '2024-01-13', views: 9560 }
180    ],
181    newest: [
182      { id: 4, title: 'Najnowszy post', description: 'Świeżo opublikowany materiał na blogu.', date: '2024-01-16', views: 245 },
183      { id: 5, title: 'Wczorajszy artykuł', description: 'Opublikowany wczoraj, już zyskuje popularność.', date: '2024-01-15', views: 1230 },
184      { id: 6, title: 'Sprzed 2 dni', description: 'Nadal aktualny i wartościowy materiał.', date: '2024-01-14', views: 2100 }
185    ],
186    trending: [
187      { id: 7, title: 'Viral na social media', description: 'Ten artykuł podbija social media!', date: '2024-01-12', views: 25600 },
188      { id: 8, title: 'Rosnąca popularność', description: 'Szybko zyskuje na popularności.', date: '2024-01-11', views: 8970 },
189      { id: 9, title: 'Trend tygodnia', description: 'Główny trend tego tygodnia w naszej branży.', date: '2024-01-10', views: 14520 }
190    ]
191  };
192
193  return contentMap[tab] || [];
194}

2. Smart filtering z debouncing

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  // Opóźnione wartości dla ciężkich operacji
9  const deferredQuery = useDeferredValue(searchQuery);
10  const deferredPriceRange = useDeferredValue(priceRange);
11  const deferredCategory = useDeferredValue(category);
12  
13  // Sprawdzenie czy filtrowanie jest w toku
14  const isFiltering = 
15    searchQuery !== deferredQuery ||
16    priceRange !== deferredPriceRange ||
17    category !== deferredCategory;
18
19  const products = useMemo(() => generateProducts(1000), []);
20
21  // Filtrowanie z opóźnionymi wartościami
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 z 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 }}>Filtry</h3>
54          
55          {/* Search Filter */}
56          <div style={{ marginBottom: '20px' }}>
57            <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
58              Szukaj:
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              Kategoria:
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">Wszystkie</option>
88              <option value="Electronics">Elektronika</option>
89              <option value="Books">Książki</option>
90              <option value="Clothing">Odzież</option>
91              <option value="Home">Dom</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              Cena: {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>🔄 Filtrowanie...</span>
120            ) : (
121              <span>✅ Gotowe</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              Produkty ({filteredProducts.length})
136            </h3>
137            
138            {isFiltering && (
139              <span style={{ color: '#007bff', fontSize: '14px' }}>
140                Aktualizowanie wyników...
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              Brak produktów spełniających kryteria
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}

Najlepsze praktyki z useDeferredValue

1. Kiedy używać useDeferredValue

1// ✅ DOBRZE - Ciężkie listy i filtrowanie
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// ✅ DOBRZE - Renderowanie wykresów
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// ❌ ŹLE - Proste operacje
28function SimpleCounter() {
29  const [count, setCount] = useState(0);
30  const deferredCount = useDeferredValue(count); // Niepotrzebne!
31  
32  return <div>{deferredCount}</div>;
33}

2. Kombinowanie z innymi hooks

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')}>Nazwa</th>
48            <th onClick={() => handleSort('category')}>Kategoria</th>
49            <th onClick={() => handleSort('price')}>Cena</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}

3. Czego unikać

1// ❌ ŹLE - Nadmierne użycie
2function OverusedDeferred() {
3  const [name, setName] = useState('');
4  const [age, setAge] = useState(0);
5  const [email, setEmail] = useState('');
6  
7  // Nie każda wartość potrzebuje 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// ✅ LEPIEJ - Selektywne użycie
16function BetterApproach() {
17  const [formData, setFormData] = useState({ name: '', age: 0, email: '' });
18  const deferredFormData = useDeferredValue(formData);
19  
20  return <HeavyValidationForm data={deferredFormData} />;
21}

useDeferredValue
to elegancki sposób na poprawę wydajności aplikacji React bez skomplikowanego zarządzania stanem. Pozwala na zachowanie responsywności krytycznych elementów UI podczas wykonywania ciężkich operacji w tle.

Przejdź do CodeWorlds