Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

use - uniwersalny hook do obsługi Promise i Context

Hook

use
to eksperymentalna funkcja React, która pozwala na "używanie" zasobów takich jak Promise czy Context. Jest to pierwszy hook, który może być wywoływany warunkowo i w pętlach, co otwiera nowe możliwości w pisaniu komponentów.

Podstawy use

1. Czytanie Promise z use

1import { use, Suspense } from 'react';
2
3// Funkcja zwracająca Promise
4function fetchUser(id) {
5  return fetch(`/api/users/${id}`).then(res => res.json());
6}
7
8// Komponent używający use z Promise
9function UserProfile({ userPromise }) {
10  // use automatycznie "rozpakowuje" Promise
11  const user = use(userPromise);
12  
13  return (
14    <div style={{ 
15      padding: '20px', 
16      border: '1px solid #ddd', 
17      borderRadius: '8px' 
18    }}>
19      <h2>{user.name}</h2>
20      <p>Email: {user.email}</p>
21      <p>Dołączył: {new Date(user.joinedDate).toLocaleDateString('pl-PL')}</p>
22    </div>
23  );
24}
25
26// Komponent rodzic
27function UserDashboard({ userId }) {
28  // Tworzymy Promise raz, aby uniknąć wielokrotnych requestów
29  const userPromise = fetchUser(userId);
30  
31  return (
32    <div>
33      <h1>Panel użytkownika</h1>
34      <Suspense fallback={<div>Ładowanie danych użytkownika...</div>}>
35        <UserProfile userPromise={userPromise} />
36      </Suspense>
37    </div>
38  );
39}

2. Używanie use z Context

1import { use, createContext, useState } from 'react';
2
3// Tworzenie Context
4const ThemeContext = createContext(null);
5const UserContext = createContext(null);
6
7// Komponent używający use z Context
8function Header() {
9  // use może być używany warunkowo!
10  const theme = use(ThemeContext);
11  const user = use(UserContext);
12  
13  return (
14    <header style={{
15      backgroundColor: theme === 'dark' ? '#333' : '#f8f9fa',
16      color: theme === 'dark' ? '#fff' : '#333',
17      padding: '20px'
18    }}>
19      <h1>Witaj, {user.name}!</h1>
20      <p>Aktualny motyw: {theme}</p>
21    </header>
22  );
23}
24
25// Możliwość warunkowego użycia
26function ConditionalComponent({ showUserInfo }) {
27  const theme = use(ThemeContext);
28  
29  // use może być używany warunkowo - to niemożliwe z innymi hooks!
30  if (showUserInfo) {
31    const user = use(UserContext);
32    return <div>Użytkownik: {user.name}</div>;
33  }
34  
35  return <div>Motyw: {theme}</div>;
36}
37
38// App z providers
39function App() {
40  const [theme, setTheme] = useState('light');
41  const [user] = useState({ name: 'Jan Kowalski', id: 1 });
42  
43  return (
44    <ThemeContext.Provider value={theme}>
45      <UserContext.Provider value={user}>
46        <div>
47          <Header />
48          <ConditionalComponent showUserInfo={true} />
49          <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
50            Zmień motyw
51          </button>
52        </div>
53      </UserContext.Provider>
54    </ThemeContext.Provider>
55  );
56}

3. use w pętlach

1import { use, Suspense } from 'react';
2
3// Funkcja tworząca Promise dla każdego posta
4function fetchPost(id) {
5  return fetch(`/api/posts/${id}`).then(res => res.json());
6}
7
8// Komponent wyświetlający wiele postów
9function PostList({ postIds }) {
10  const posts = postIds.map(id => {
11    // use może być używany w pętlach!
12    const postPromise = fetchPost(id);
13    // eslint-disable-next-line react-hooks/rules-of-hooks
14    const post = use(postPromise);
15    return post;
16  });
17  
18  return (
19    <div>
20      {posts.map(post => (
21        <article 
22          key={post.id}
23          style={{ 
24            marginBottom: '20px',
25            padding: '15px',
26            backgroundColor: '#f8f9fa',
27            borderRadius: '8px'
28          }}
29        >
30          <h3>{post.title}</h3>
31          <p>{post.content}</p>
32          <small>Autor: {post.author}</small>
33        </article>
34      ))}
35    </div>
36  );
37}
38
39// Wrapper z Suspense
40function BlogSection() {
41  const postIds = [1, 2, 3, 4, 5];
42  
43  return (
44    <section>
45      <h2>Najnowsze posty</h2>
46      <Suspense fallback={<PostListSkeleton count={5} />}>
47        <PostList postIds={postIds} />
48      </Suspense>
49    </section>
50  );
51}
52
53function PostListSkeleton({ count }) {
54  return (
55    <div>
56      {Array.from({ length: count }).map((_, i) => (
57        <div 
58          key={i}
59          style={{ 
60            marginBottom: '20px',
61            padding: '15px',
62            backgroundColor: '#e9ecef',
63            borderRadius: '8px',
64            height: '100px'
65          }}
66        />
67      ))}
68    </div>
69  );
70}

Zaawansowane wzorce z use

1. Cache dla Promise

1import { use, Suspense, useState } from 'react';
2
3// Prosty cache dla Promise
4const promiseCache = new Map();
5
6function getCachedPromise(key, promiseFactory) {
7  if (!promiseCache.has(key)) {
8    promiseCache.set(key, promiseFactory());
9  }
10  return promiseCache.get(key);
11}
12
13// API functions
14function fetchUserData(userId) {
15  const cached = getCachedPromise(
16    `user-${userId}`,
17    () => fetch(`/api/users/${userId}`).then(res => res.json())
18  );
19  return cached;
20}
21
22function fetchUserPosts(userId) {
23  const cached = getCachedPromise(
24    `posts-${userId}`,
25    () => fetch(`/api/users/${userId}/posts`).then(res => res.json())
26  );
27  return cached;
28}
29
30// Komponenty
31function UserInfo({ userId }) {
32  const userPromise = fetchUserData(userId);
33  const user = use(userPromise);
34  
35  return (
36    <div style={{ 
37      padding: '20px',
38      backgroundColor: '#f8f9fa',
39      borderRadius: '8px',
40      marginBottom: '20px'
41    }}>
42      <h2>{user.name}</h2>
43      <p>📧 {user.email}</p>
44      <p>📍 {user.location}</p>
45      <p>🎂 Dołączył: {new Date(user.joinedDate).toLocaleDateString('pl-PL')}</p>
46    </div>
47  );
48}
49
50function UserPosts({ userId }) {
51  const postsPromise = fetchUserPosts(userId);
52  const posts = use(postsPromise);
53  
54  return (
55    <div>
56      <h3>Posty użytkownika ({posts.length})</h3>
57      {posts.map(post => (
58        <article 
59          key={post.id}
60          style={{ 
61            marginBottom: '15px',
62            padding: '15px',
63            border: '1px solid #e9ecef',
64            borderRadius: '6px'
65          }}
66        >
67          <h4>{post.title}</h4>
68          <p>{post.excerpt}</p>
69          <small>📅 {new Date(post.date).toLocaleDateString('pl-PL')}</small>
70        </article>
71      ))}
72    </div>
73  );
74}
75
76function UserProfilePage() {
77  const [userId, setUserId] = useState(1);
78  
79  return (
80    <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
81      <div style={{ marginBottom: '20px' }}>
82        <button onClick={() => setUserId(1)} disabled={userId === 1}>
83          Użytkownik 1
84        </button>
85        <button onClick={() => setUserId(2)} disabled={userId === 2}>
86          Użytkownik 2
87        </button>
88        <button onClick={() => promiseCache.clear()}>
89          Wyczyść cache
90        </button>
91      </div>
92      
93      <Suspense fallback={<div>Ładowanie profilu...</div>}>
94        <UserInfo userId={userId} />
95      </Suspense>
96      
97      <Suspense fallback={<div>Ładowanie postów...</div>}>
98        <UserPosts userId={userId} />
99      </Suspense>
100    </div>
101  );
102}

2. Równoległe ładowanie danych

1import { use, Suspense } from 'react';
2
3// Funkcje API
4async function fetchDashboardData() {
5  const [stats, activities, notifications] = await Promise.all([
6    fetch('/api/stats').then(res => res.json()),
7    fetch('/api/activities').then(res => res.json()),
8    fetch('/api/notifications').then(res => res.json())
9  ]);
10  
11  return { stats, activities, notifications };
12}
13
14// Komponent dashboardu
15function Dashboard({ dataPromise }) {
16  const { stats, activities, notifications } = use(dataPromise);
17  
18  return (
19    <div style={{ 
20      display: 'grid', 
21      gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
22      gap: '20px',
23      padding: '20px'
24    }}>
25      <StatsCard stats={stats} />
26      <ActivitiesCard activities={activities} />
27      <NotificationsCard notifications={notifications} />
28    </div>
29  );
30}
31
32function StatsCard({ stats }) {
33  return (
34    <div style={{ 
35      padding: '20px',
36      backgroundColor: '#f8f9fa',
37      borderRadius: '8px',
38      boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
39    }}>
40      <h3>📊 Statystyki</h3>
41      <div style={{ display: 'grid', gap: '10px' }}>
42        <div>
43          <strong>Użytkownicy:</strong> {stats.users.toLocaleString('pl-PL')}
44        </div>
45        <div>
46          <strong>Przychód:</strong> {stats.revenue.toLocaleString('pl-PL', {
47            style: 'currency',
48            currency: 'PLN'
49          })}
50        </div>
51        <div>
52          <strong>Wzrost:</strong> 
53          <span style={{ color: stats.growth > 0 ? '#28a745' : '#dc3545' }}>
54            {stats.growth > 0 ? '+' : ''}{stats.growth}%
55          </span>
56        </div>
57      </div>
58    </div>
59  );
60}
61
62function ActivitiesCard({ activities }) {
63  return (
64    <div style={{ 
65      padding: '20px',
66      backgroundColor: '#f8f9fa',
67      borderRadius: '8px',
68      boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
69    }}>
70      <h3>🎯 Ostatnie aktywności</h3>
71      <ul style={{ listStyle: 'none', padding: 0 }}>
72        {activities.slice(0, 5).map(activity => (
73          <li 
74            key={activity.id}
75            style={{ 
76              padding: '8px 0',
77              borderBottom: '1px solid #e9ecef'
78            }}
79          >
80            <div style={{ fontWeight: '500' }}>{activity.user}</div>
81            <div style={{ fontSize: '14px', color: '#6c757d' }}>
82              {activity.action}
83            </div>
84            <small style={{ color: '#6c757d' }}>
85              {new Date(activity.timestamp).toLocaleString('pl-PL')}
86            </small>
87          </li>
88        ))}
89      </ul>
90    </div>
91  );
92}
93
94function NotificationsCard({ notifications }) {
95  const unreadCount = notifications.filter(n => !n.read).length;
96  
97  return (
98    <div style={{ 
99      padding: '20px',
100      backgroundColor: '#f8f9fa',
101      borderRadius: '8px',
102      boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
103    }}>
104      <h3>
105        🔔 Powiadomienia 
106        {unreadCount > 0 && (
107          <span style={{ 
108            marginLeft: '10px',
109            padding: '2px 8px',
110            backgroundColor: '#dc3545',
111            color: 'white',
112            borderRadius: '12px',
113            fontSize: '14px'
114          }}>
115            {unreadCount}
116          </span>
117        )}
118      </h3>
119      <div style={{ maxHeight: '200px', overflowY: 'auto' }}>
120        {notifications.map(notification => (
121          <div 
122            key={notification.id}
123            style={{ 
124              padding: '10px',
125              marginBottom: '8px',
126              backgroundColor: notification.read ? 'transparent' : '#e3f2fd',
127              borderRadius: '4px',
128              fontSize: '14px'
129            }}
130          >
131            <strong>{notification.title}</strong>
132            <p style={{ margin: '4px 0' }}>{notification.message}</p>
133          </div>
134        ))}
135      </div>
136    </div>
137  );
138}
139
140// Główny komponent
141function DashboardApp() {
142  const dataPromise = fetchDashboardData();
143  
144  return (
145    <div>
146      <h1 style={{ padding: '20px' }}>Panel administracyjny</h1>
147      <Suspense fallback={<DashboardSkeleton />}>
148        <Dashboard dataPromise={dataPromise} />
149      </Suspense>
150    </div>
151  );
152}
153
154function DashboardSkeleton() {
155  return (
156    <div style={{ 
157      display: 'grid', 
158      gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
159      gap: '20px',
160      padding: '20px'
161    }}>
162      {[1, 2, 3].map(i => (
163        <div 
164          key={i}
165          style={{ 
166            height: '250px',
167            backgroundColor: '#e9ecef',
168            borderRadius: '8px',
169            animation: 'pulse 1.5s ease-in-out infinite'
170          }}
171        />
172      ))}
173    </div>
174  );
175}

3. Error boundaries z use

1import { use, Suspense, Component } from 'react';
2
3// Error Boundary
4class ErrorBoundary extends Component {
5  constructor(props) {
6    super(props);
7    this.state = { hasError: false, error: null };
8  }
9  
10  static getDerivedStateFromError(error) {
11    return { hasError: true, error };
12  }
13  
14  componentDidCatch(error, errorInfo) {
15    console.error('Error caught by boundary:', error, errorInfo);
16  }
17  
18  render() {
19    if (this.state.hasError) {
20      return (
21        <div style={{ 
22          padding: '20px',
23          backgroundColor: '#f8d7da',
24          border: '1px solid #f5c6cb',
25          borderRadius: '8px',
26          color: '#721c24'
27        }}>
28          <h3>❌ Coś poszło nie tak</h3>
29          <p>{this.state.error?.message || 'Nieznany błąd'}</p>
30          <button 
31            onClick={() => this.setState({ hasError: false, error: null })}
32            style={{
33              marginTop: '10px',
34              padding: '8px 16px',
35              backgroundColor: '#721c24',
36              color: 'white',
37              border: 'none',
38              borderRadius: '4px',
39              cursor: 'pointer'
40            }}
41          >
42            Spróbuj ponownie
43          </button>
44        </div>
45      );
46    }
47    
48    return this.props.children;
49  }
50}
51
52// Funkcja API która może zwrócić błąd
53function fetchDataWithError(shouldFail = false) {
54  return new Promise((resolve, reject) => {
55    setTimeout(() => {
56      if (shouldFail) {
57        reject(new Error('Błąd pobierania danych z serwera'));
58      } else {
59        resolve({
60          message: 'Dane załadowane pomyślnie!',
61          timestamp: new Date().toISOString()
62        });
63      }
64    }, 1000);
65  });
66}
67
68// Komponent używający use
69function DataDisplay({ dataPromise }) {
70  const data = use(dataPromise);
71  
72  return (
73    <div style={{ 
74      padding: '20px',
75      backgroundColor: '#d4edda',
76      border: '1px solid #c3e6cb',
77      borderRadius: '8px',
78      color: '#155724'
79    }}>
80      <h3>✅ Sukces!</h3>
81      <p>{data.message}</p>
82      <small>Załadowano: {new Date(data.timestamp).toLocaleString('pl-PL')}</small>
83    </div>
84  );
85}
86
87// Główny komponent
88function ErrorHandlingDemo() {
89  const [shouldFail, setShouldFail] = useState(false);
90  const [key, setKey] = useState(0);
91  
92  const dataPromise = fetchDataWithError(shouldFail);
93  
94  return (
95    <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
96      <h2>Obsługa błędów z use</h2>
97      
98      <div style={{ marginBottom: '20px' }}>
99        <label>
100          <input
101            type="checkbox"
102            checked={shouldFail}
103            onChange={(e) => setShouldFail(e.target.checked)}
104          />
105          Symuluj błąd
106        </label>
107        <button 
108          onClick={() => setKey(k => k + 1)}
109          style={{ marginLeft: '20px' }}
110        >
111          Odśwież
112        </button>
113      </div>
114      
115      <ErrorBoundary key={key}>
116        <Suspense fallback={
117          <div style={{ 
118            padding: '20px',
119            backgroundColor: '#cfe2ff',
120            border: '1px solid #b6d4fe',
121            borderRadius: '8px',
122            color: '#084298'
123          }}>
124            ⏳ Ładowanie danych...
125          </div>
126        }>
127          <DataDisplay dataPromise={dataPromise} />
128        </Suspense>
129      </ErrorBoundary>
130    </div>
131  );
132}

Praktyczne zastosowania use

1. System routingu z lazy loading

1import { use, Suspense, lazy, useState } from 'react';
2
3// Symulacja systemu routingu
4const routes = {
5  '/': lazy(() => import('./pages/Home')),
6  '/about': lazy(() => import('./pages/About')),
7  '/contact': lazy(() => import('./pages/Contact')),
8  '/profile': lazy(() => import('./pages/Profile'))
9};
10
11// Context dla routingu
12const RouteContext = createContext(null);
13
14// Hook do nawigacji
15function useNavigation() {
16  const { navigate } = use(RouteContext);
17  return navigate;
18}
19
20// Komponent strony
21function Page({ path }) {
22  const Component = routes[path] || routes['/'];
23  
24  return (
25    <Suspense fallback={<PageLoader />}>
26      <Component />
27    </Suspense>
28  );
29}
30
31function PageLoader() {
32  return (
33    <div style={{ 
34      display: 'flex',
35      justifyContent: 'center',
36      alignItems: 'center',
37      minHeight: '400px'
38    }}>
39      <div style={{ textAlign: 'center' }}>
40        <div style={{ 
41          width: '50px',
42          height: '50px',
43          border: '3px solid #f3f3f3',
44          borderTop: '3px solid #007bff',
45          borderRadius: '50%',
46          animation: 'spin 1s linear infinite',
47          margin: '0 auto 20px'
48        }} />
49        <p>Ładowanie strony...</p>
50      </div>
51    </div>
52  );
53}
54
55// Główna aplikacja
56function RouterApp() {
57  const [currentPath, setCurrentPath] = useState('/');
58  
59  const navigate = (path) => {
60    setCurrentPath(path);
61    window.history.pushState({}, '', path);
62  };
63  
64  return (
65    <RouteContext.Provider value={{ navigate, currentPath }}>
66      <div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
67        <Navigation />
68        <main style={{ flex: 1, padding: '20px' }}>
69          <Page path={currentPath} />
70        </main>
71      </div>
72    </RouteContext.Provider>
73  );
74}
75
76function Navigation() {
77  const navigate = useNavigation();
78  const { currentPath } = use(RouteContext);
79  
80  const links = [
81    { path: '/', label: 'Strona główna' },
82    { path: '/about', label: 'O nas' },
83    { path: '/contact', label: 'Kontakt' },
84    { path: '/profile', label: 'Profil' }
85  ];
86  
87  return (
88    <nav style={{ 
89      backgroundColor: '#333',
90      padding: '10px 20px'
91    }}>
92      {links.map(link => (
93        <button
94          key={link.path}
95          onClick={() => navigate(link.path)}
96          style={{
97            marginRight: '10px',
98            padding: '8px 16px',
99            backgroundColor: currentPath === link.path ? '#007bff' : 'transparent',
100            color: 'white',
101            border: 'none',
102            borderRadius: '4px',
103            cursor: 'pointer'
104          }}
105        >
106          {link.label}
107        </button>
108      ))}
109    </nav>
110  );
111}

2. Dynamiczne formularze z walidacją

1import { use, createContext, useState } from 'react';
2
3// Context dla formularza
4const FormContext = createContext(null);
5
6// Hook do zarządzania formularzem
7function useFormField(name) {
8  const form = use(FormContext);
9  
10  return {
11    value: form.values[name] || '',
12    error: form.errors[name],
13    onChange: (e) => form.setValue(name, e.target.value),
14    onBlur: () => form.validateField(name)
15  };
16}
17
18// Komponent pola formularza
19function FormField({ name, label, type = 'text', validate }) {
20  const field = useFormField(name);
21  const form = use(FormContext);
22  
23  // Rejestracja walidatora
24  if (validate && !form.validators[name]) {
25    form.registerValidator(name, validate);
26  }
27  
28  return (
29    <div style={{ marginBottom: '15px' }}>
30      <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>
31        {label}
32      </label>
33      <input
34        type={type}
35        value={field.value}
36        onChange={field.onChange}
37        onBlur={field.onBlur}
38        style={{
39          width: '100%',
40          padding: '8px',
41          border: `1px solid ${field.error ? '#dc3545' : '#ddd'}`,
42          borderRadius: '4px'
43        }}
44      />
45      {field.error && (
46        <div style={{ color: '#dc3545', fontSize: '14px', marginTop: '5px' }}>
47          {field.error}
48        </div>
49      )}
50    </div>
51  );
52}
53
54// Provider formularza
55function FormProvider({ children, onSubmit }) {
56  const [values, setValues] = useState({});
57  const [errors, setErrors] = useState({});
58  const [validators] = useState({});
59  
60  const setValue = (name, value) => {
61    setValues(prev => ({ ...prev, [name]: value }));
62    // Czyść błąd przy zmianie wartości
63    setErrors(prev => ({ ...prev, [name]: '' }));
64  };
65  
66  const registerValidator = (name, validate) => {
67    validators[name] = validate;
68  };
69  
70  const validateField = (name) => {
71    if (validators[name]) {
72      const error = validators[name](values[name]);
73      setErrors(prev => ({ ...prev, [name]: error }));
74      return !error;
75    }
76    return true;
77  };
78  
79  const validateAll = () => {
80    const newErrors = {};
81    let isValid = true;
82    
83    Object.keys(validators).forEach(name => {
84      const error = validators[name](values[name]);
85      if (error) {
86        newErrors[name] = error;
87        isValid = false;
88      }
89    });
90    
91    setErrors(newErrors);
92    return isValid;
93  };
94  
95  const handleSubmit = (e) => {
96    e.preventDefault();
97    if (validateAll()) {
98      onSubmit(values);
99    }
100  };
101  
102  const formValue = {
103    values,
104    errors,
105    validators,
106    setValue,
107    registerValidator,
108    validateField,
109    handleSubmit
110  };
111  
112  return (
113    <FormContext.Provider value={formValue}>
114      <form onSubmit={handleSubmit}>
115        {children}
116      </form>
117    </FormContext.Provider>
118  );
119}
120
121// Przykład użycia
122function RegistrationFormExample() {
123  const handleSubmit = (values) => {
124    console.log('Form submitted:', values);
125    alert('Rejestracja zakończona sukcesem!');
126  };
127  
128  return (
129    <div style={{ maxWidth: '400px', margin: '0 auto', padding: '20px' }}>
130      <h2>Formularz rejestracji</h2>
131      
132      <FormProvider onSubmit={handleSubmit}>
133        <FormField
134          name="email"
135          label="Email"
136          type="email"
137          validate={(value) => {
138            if (!value) return 'Email jest wymagany';
139            if (!/S+@S+.S+/.test(value)) return 'Nieprawidłowy email';
140            return '';
141          }}
142        />
143        
144        <FormField
145          name="password"
146          label="Hasło"
147          type="password"
148          validate={(value) => {
149            if (!value) return 'Hasło jest wymagane';
150            if (value.length < 8) return 'Hasło musi mieć minimum 8 znaków';
151            return '';
152          }}
153        />
154        
155        <FormField
156          name="confirmPassword"
157          label="Potwierdź hasło"
158          type="password"
159          validate={(value) => {
160            const form = use(FormContext);
161            if (value !== form.values.password) return 'Hasła nie są identyczne';
162            return '';
163          }}
164        />
165        
166        <button
167          type="submit"
168          style={{
169            width: '100%',
170            padding: '10px',
171            backgroundColor: '#007bff',
172            color: 'white',
173            border: 'none',
174            borderRadius: '4px',
175            fontSize: '16px',
176            cursor: 'pointer'
177          }}
178        >
179          Zarejestruj się
180        </button>
181      </FormProvider>
182    </div>
183  );
184}

Najlepsze praktyki z use

1. Kiedy używać use

1// ✅ DOBRZE - warunkowe użycie
2function ConditionalData({ showDetails }) {
3  if (showDetails) {
4    const details = use(detailsPromise);
5    return <DetailView details={details} />;
6  }
7  return <Summary />;
8}
9
10// ✅ DOBRZE - w pętlach
11function MultipleResources({ resourceIds }) {
12  const resources = resourceIds.map(id => {
13    const promise = fetchResource(id);
14    return use(promise);
15  });
16  return <ResourceList resources={resources} />;
17}
18
19// ❌ ŹLE - niepotrzebne użycie dla prostych wartości
20function BadExample() {
21  const value = use(Promise.resolve(42)); // Niepotrzebne!
22  return <div>{value}</div>;
23}

2. Cache i optymalizacja

1// Implementacja prostego cache
2const cache = new Map();
3
4function useCachedResource(key, fetcher) {
5  if (!cache.has(key)) {
6    cache.set(key, fetcher());
7  }
8  
9  const promise = cache.get(key);
10  const resource = use(promise);
11  
12  return resource;
13}
14
15// Użycie
16function CachedDataComponent({ userId }) {
17  const userData = useCachedResource(
18    `user-${userId}`,
19    () => fetchUser(userId)
20  );
21  
22  return <UserProfile user={userData} />;
23}

3. Obsługa błędów

1// Wrapper z retry logic
2function createRetryablePromise(fetcher, maxRetries = 3) {
3  return async () => {
4    let lastError;
5    
6    for (let i = 0; i < maxRetries; i++) {
7      try {
8        return await fetcher();
9      } catch (error) {
10        lastError = error;
11        if (i < maxRetries - 1) {
12          // Exponential backoff
13          await new Promise(resolve => 
14            setTimeout(resolve, Math.pow(2, i) * 1000)
15          );
16        }
17      }
18    }
19    
20    throw lastError;
21  };
22}
23
24// Użycie
25function RobustDataFetching() {
26  const dataPromise = createRetryablePromise(
27    () => fetch('/api/critical-data').then(res => res.json())
28  )();
29  
30  return (
31    <ErrorBoundary>
32      <Suspense fallback={<div>Ładowanie...</div>}>
33        <DataComponent dataPromise={dataPromise} />
34      </Suspense>
35    </ErrorBoundary>
36  );
37}

Hook

use
otwiera nowe możliwości w React, pozwalając na bardziej elastyczne zarządzanie asynchronicznymi danymi i kontekstem. Jego unikalna zdolność do bycia używanym warunkowo i w pętlach czyni go potężnym narzędziem dla zaawansowanych aplikacji.

Vai a CodeWorlds