npm-react

Description
React is a JavaScript library for building user interfaces with declarative, component-based architecture.
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-react@18.3.0

context.md docs/

1
# Context API
2
3
React Context provides a way to share data across the component tree without passing props through every level. It's ideal for global state like themes, authentication, or user preferences.
4
5
## Capabilities
6
7
### createContext
8
9
Creates a new React context object with Provider and Consumer components.
10
11
```javascript { .api }
12
/**
13
* Creates a React context for sharing data across component tree
14
* @param defaultValue - Default value when no Provider is found
15
* @returns Context object with Provider and Consumer
16
*/
17
function createContext<T>(defaultValue: T): Context<T>;
18
19
interface Context<T> {
20
Provider: ExoticComponent<ProviderProps<T>>;
21
Consumer: ExoticComponent<ConsumerProps<T>>;
22
displayName?: string;
23
}
24
25
interface ProviderProps<T> {
26
value: T;
27
children?: ReactNode;
28
}
29
30
interface ConsumerProps<T> {
31
children: (value: T) => ReactNode;
32
}
33
```
34
35
**Usage Examples:**
36
37
```javascript
38
import React, { createContext, useContext, useState } from 'react';
39
40
// Create theme context
41
const ThemeContext = createContext('light');
42
43
function App() {
44
const [theme, setTheme] = useState('light');
45
46
return (
47
<ThemeContext.Provider value={theme}>
48
<Header />
49
<MainContent />
50
<ThemeToggle onToggle={() => setTheme(theme === 'light' ? 'dark' : 'light')} />
51
</ThemeContext.Provider>
52
);
53
}
54
55
function Header() {
56
const theme = useContext(ThemeContext);
57
58
return (
59
<header className={`header-${theme}`}>
60
<h1>My App</h1>
61
</header>
62
);
63
}
64
65
// Complex context with multiple values
66
const UserContext = createContext({
67
user: null,
68
login: () => {},
69
logout: () => {},
70
isAuthenticated: false
71
});
72
73
function UserProvider({ children }) {
74
const [user, setUser] = useState(null);
75
76
const login = async (credentials) => {
77
const userData = await authenticateUser(credentials);
78
setUser(userData);
79
};
80
81
const logout = () => {
82
setUser(null);
83
};
84
85
const value = {
86
user,
87
login,
88
logout,
89
isAuthenticated: !!user
90
};
91
92
return (
93
<UserContext.Provider value={value}>
94
{children}
95
</UserContext.Provider>
96
);
97
}
98
99
// Custom hook for using context
100
function useUser() {
101
const context = useContext(UserContext);
102
if (!context) {
103
throw new Error('useUser must be used within UserProvider');
104
}
105
return context;
106
}
107
108
// Component using the context
109
function UserProfile() {
110
const { user, logout, isAuthenticated } = useUser();
111
112
if (!isAuthenticated) {
113
return <div>Please log in</div>;
114
}
115
116
return (
117
<div>
118
<h2>Welcome, {user.name}</h2>
119
<button onClick={logout}>Logout</button>
120
</div>
121
);
122
}
123
```
124
125
### Consumer Pattern
126
127
Using Context.Consumer for consuming context values (alternative to useContext hook).
128
129
```javascript { .api }
130
/**
131
* Context Consumer component for accessing context values
132
* @param children - Render function that receives context value
133
*/
134
interface ConsumerProps<T> {
135
children: (value: T) => ReactNode;
136
}
137
```
138
139
**Usage Examples:**
140
141
```javascript
142
import React, { createContext } from 'react';
143
144
const ConfigContext = createContext({
145
apiUrl: 'https://api.example.com',
146
timeout: 5000,
147
retries: 3
148
});
149
150
// Using Consumer component
151
function ApiStatus() {
152
return (
153
<ConfigContext.Consumer>
154
{(config) => (
155
<div>
156
<p>API URL: {config.apiUrl}</p>
157
<p>Timeout: {config.timeout}ms</p>
158
<p>Retries: {config.retries}</p>
159
</div>
160
)}
161
</ConfigContext.Consumer>
162
);
163
}
164
165
// Multiple contexts with Consumer
166
function UserDisplay() {
167
return (
168
<ThemeContext.Consumer>
169
{(theme) => (
170
<UserContext.Consumer>
171
{(user) => (
172
<div className={`user-display theme-${theme}`}>
173
<h3>{user.name}</h3>
174
<p>{user.email}</p>
175
</div>
176
)}
177
</UserContext.Consumer>
178
)}
179
</ThemeContext.Consumer>
180
);
181
}
182
183
// Render prop pattern with Consumer
184
function withConfig(Component) {
185
return function ConfiguredComponent(props) {
186
return (
187
<ConfigContext.Consumer>
188
{(config) => <Component {...props} config={config} />}
189
</ConfigContext.Consumer>
190
);
191
};
192
}
193
194
const ConfiguredApiClient = withConfig(ApiClient);
195
```
196
197
### Context with Reducers
198
199
Complex state management using context with useReducer.
200
201
```javascript { .api }
202
/**
203
* Context pattern with reducer for complex state management
204
*/
205
// No specific API - this is a pattern using createContext with useReducer
206
```
207
208
**Usage Examples:**
209
210
```javascript
211
import React, { createContext, useContext, useReducer } from 'react';
212
213
// Shopping cart context with reducer
214
const CartContext = createContext();
215
216
const cartReducer = (state, action) => {
217
switch (action.type) {
218
case 'ADD_ITEM':
219
const existingItem = state.items.find(item => item.id === action.payload.id);
220
if (existingItem) {
221
return {
222
...state,
223
items: state.items.map(item =>
224
item.id === action.payload.id
225
? { ...item, quantity: item.quantity + 1 }
226
: item
227
)
228
};
229
}
230
return {
231
...state,
232
items: [...state.items, { ...action.payload, quantity: 1 }]
233
};
234
235
case 'REMOVE_ITEM':
236
return {
237
...state,
238
items: state.items.filter(item => item.id !== action.payload)
239
};
240
241
case 'UPDATE_QUANTITY':
242
return {
243
...state,
244
items: state.items.map(item =>
245
item.id === action.payload.id
246
? { ...item, quantity: action.payload.quantity }
247
: item
248
)
249
};
250
251
case 'CLEAR_CART':
252
return { ...state, items: [] };
253
254
default:
255
return state;
256
}
257
};
258
259
function CartProvider({ children }) {
260
const [state, dispatch] = useReducer(cartReducer, {
261
items: [],
262
isOpen: false
263
});
264
265
const addItem = (product) => {
266
dispatch({ type: 'ADD_ITEM', payload: product });
267
};
268
269
const removeItem = (productId) => {
270
dispatch({ type: 'REMOVE_ITEM', payload: productId });
271
};
272
273
const updateQuantity = (productId, quantity) => {
274
dispatch({ type: 'UPDATE_QUANTITY', payload: { id: productId, quantity } });
275
};
276
277
const clearCart = () => {
278
dispatch({ type: 'CLEAR_CART' });
279
};
280
281
const getTotalItems = () => {
282
return state.items.reduce((total, item) => total + item.quantity, 0);
283
};
284
285
const getTotalPrice = () => {
286
return state.items.reduce((total, item) => total + (item.price * item.quantity), 0);
287
};
288
289
const value = {
290
items: state.items,
291
addItem,
292
removeItem,
293
updateQuantity,
294
clearCart,
295
getTotalItems,
296
getTotalPrice
297
};
298
299
return (
300
<CartContext.Provider value={value}>
301
{children}
302
</CartContext.Provider>
303
);
304
}
305
306
function useCart() {
307
const context = useContext(CartContext);
308
if (!context) {
309
throw new Error('useCart must be used within CartProvider');
310
}
311
return context;
312
}
313
314
// Usage in components
315
function ProductCard({ product }) {
316
const { addItem } = useCart();
317
318
return (
319
<div className="product-card">
320
<h3>{product.name}</h3>
321
<p>${product.price}</p>
322
<button onClick={() => addItem(product)}>
323
Add to Cart
324
</button>
325
</div>
326
);
327
}
328
329
function CartSummary() {
330
const { items, getTotalItems, getTotalPrice, clearCart } = useCart();
331
332
return (
333
<div className="cart-summary">
334
<h3>Cart ({getTotalItems()} items)</h3>
335
<p>Total: ${getTotalPrice().toFixed(2)}</p>
336
{items.length > 0 && (
337
<button onClick={clearCart}>Clear Cart</button>
338
)}
339
</div>
340
);
341
}
342
```
343
344
### createServerContext
345
346
Creates server-specific context for server-side rendering scenarios.
347
348
```javascript { .api }
349
/**
350
* Creates server-specific context for SSR
351
* @param globalName - Global identifier for the context
352
* @param defaultValue - Default context value
353
* @returns Server context object
354
*/
355
function createServerContext<T>(globalName: string, defaultValue: T): ServerContext<T>;
356
357
interface ServerContext<T> {
358
Provider: ExoticComponent<ProviderProps<T>>;
359
Consumer: ExoticComponent<ConsumerProps<T>>;
360
displayName?: string;
361
}
362
```
363
364
**Usage Examples:**
365
366
```javascript
367
import React, { createServerContext, useContext } from 'react';
368
369
// Server-specific context for request data
370
const RequestContext = createServerContext('RequestContext', {
371
url: '',
372
method: 'GET',
373
headers: {},
374
userAgent: ''
375
});
376
377
// Server-side provider
378
function ServerApp({ request, children }) {
379
const requestData = {
380
url: request.url,
381
method: request.method,
382
headers: request.headers,
383
userAgent: request.get('User-Agent') || ''
384
};
385
386
return (
387
<RequestContext.Provider value={requestData}>
388
{children}
389
</RequestContext.Provider>
390
);
391
}
392
393
// Component that uses server context
394
function RequestInfo() {
395
const request = useContext(RequestContext);
396
397
return (
398
<div>
399
<p>URL: {request.url}</p>
400
<p>Method: {request.method}</p>
401
<p>User Agent: {request.userAgent}</p>
402
</div>
403
);
404
}
405
406
// Server context for localization
407
const LocaleContext = createServerContext('LocaleContext', {
408
locale: 'en',
409
currency: 'USD',
410
timezone: 'UTC'
411
});
412
413
function LocalizedApp({ locale, children }) {
414
const localeData = {
415
locale: locale || 'en',
416
currency: getCurrencyForLocale(locale),
417
timezone: getTimezoneForLocale(locale)
418
};
419
420
return (
421
<LocaleContext.Provider value={localeData}>
422
{children}
423
</LocaleContext.Provider>
424
);
425
}
426
427
function PriceDisplay({ amount }) {
428
const { currency, locale } = useContext(LocaleContext);
429
430
const formattedPrice = new Intl.NumberFormat(locale, {
431
style: 'currency',
432
currency: currency
433
}).format(amount);
434
435
return <span>{formattedPrice}</span>;
436
}
437
```
438
439
### Context Best Practices
440
441
Common patterns and best practices for using React Context effectively.
442
443
**Usage Examples:**
444
445
```javascript
446
// 1. Separate contexts for different concerns
447
const AuthContext = createContext();
448
const ThemeContext = createContext();
449
const SettingsContext = createContext();
450
451
// 2. Custom hooks with error boundaries
452
function useAuth() {
453
const context = useContext(AuthContext);
454
if (context === undefined) {
455
throw new Error('useAuth must be used within an AuthProvider');
456
}
457
return context;
458
}
459
460
// 3. Context composition for multiple providers
461
function AppProviders({ children }) {
462
return (
463
<AuthProvider>
464
<ThemeProvider>
465
<SettingsProvider>
466
<I18nProvider>
467
{children}
468
</I18nProvider>
469
</SettingsProvider>
470
</ThemeProvider>
471
</AuthProvider>
472
);
473
}
474
475
// 4. Memoized context values to prevent unnecessary re-renders
476
function ThemeProvider({ children }) {
477
const [theme, setTheme] = useState('light');
478
479
const value = useMemo(
480
() => ({
481
theme,
482
setTheme,
483
toggleTheme: () => setTheme(prev => prev === 'light' ? 'dark' : 'light')
484
}),
485
[theme]
486
);
487
488
return (
489
<ThemeContext.Provider value={value}>
490
{children}
491
</ThemeContext.Provider>
492
);
493
}
494
495
// 5. Split context to avoid unnecessary re-renders
496
const StateContext = createContext();
497
const DispatchContext = createContext();
498
499
function StateProvider({ children }) {
500
const [state, dispatch] = useReducer(reducer, initialState);
501
502
return (
503
<StateContext.Provider value={state}>
504
<DispatchContext.Provider value={dispatch}>
505
{children}
506
</DispatchContext.Provider>
507
</StateContext.Provider>
508
);
509
}
510
511
function useState() {
512
const context = useContext(StateContext);
513
if (!context) {
514
throw new Error('useState must be used within StateProvider');
515
}
516
return context;
517
}
518
519
function useDispatch() {
520
const context = useContext(DispatchContext);
521
if (!context) {
522
throw new Error('useDispatch must be used within StateProvider');
523
}
524
return context;
525
}
526
```
527
528
## Types
529
530
```javascript { .api }
531
// Context types
532
interface Context<T> {
533
Provider: ExoticComponent<ProviderProps<T>>;
534
Consumer: ExoticComponent<ConsumerProps<T>>;
535
displayName?: string;
536
}
537
538
interface ServerContext<T> extends Context<T> {}
539
540
interface ProviderProps<T> {
541
value: T;
542
children?: ReactNode;
543
}
544
545
interface ConsumerProps<T> {
546
children: (value: T) => ReactNode;
547
}
548
549
// Context hook type
550
function useContext<T>(context: Context<T>): T;
551
```