0
# Context API
1
2
React-compatible context system for managing and sharing state across component trees without prop drilling. Enables efficient data flow from parent components to deeply nested children.
3
4
## Capabilities
5
6
### Create Context
7
8
Creates a context object containing Provider and Consumer components for sharing data across component trees.
9
10
```javascript { .api }
11
/**
12
* Creates a context object for sharing data across component trees
13
* @param defaultValue - Default value used when no Provider is found in tree
14
* @returns Context object with Provider, Consumer, and internal properties
15
*/
16
function createContext(defaultValue);
17
```
18
19
The returned context object has the following structure:
20
21
```javascript { .api }
22
interface ContextObject<T> {
23
/**
24
* Provider component that supplies context value to child components
25
*/
26
Provider: ComponentClass<ProviderProps<T>>;
27
28
/**
29
* Consumer component that receives context value via render prop
30
*/
31
Consumer: ComponentClass<ConsumerProps<T>>;
32
33
/**
34
* Internal context identifier (used by Rax internally)
35
*/
36
_contextID: string;
37
38
/**
39
* Default value for the context (used by Rax internally)
40
*/
41
_defaultValue: T;
42
43
/**
44
* Internal method for finding nearest provider (used by Rax internally)
45
*/
46
__getNearestParentProvider: Function;
47
}
48
49
interface ProviderProps<T> {
50
value: T;
51
children?: any;
52
}
53
54
interface ConsumerProps<T> {
55
children: (value: T) => any;
56
}
57
```
58
59
**Usage Examples:**
60
61
```javascript
62
import { createElement, createContext, useState } from 'rax';
63
64
// Create context with default value
65
const ThemeContext = createContext('light');
66
const UserContext = createContext(null);
67
68
// Theme Provider Component
69
function ThemeProvider({ children }) {
70
const [theme, setTheme] = useState('light');
71
72
const toggleTheme = () => {
73
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
74
};
75
76
const contextValue = {
77
theme,
78
toggleTheme
79
};
80
81
return createElement(ThemeContext.Provider, { value: contextValue }, children);
82
}
83
84
// User Provider Component
85
function UserProvider({ children }) {
86
const [user, setUser] = useState({ name: 'John Doe', id: 1 });
87
88
return createElement(UserContext.Provider, { value: { user, setUser } }, children);
89
}
90
91
// App with multiple providers
92
function App() {
93
return createElement(ThemeProvider, null,
94
createElement(UserProvider, null,
95
createElement(Dashboard)
96
)
97
);
98
}
99
```
100
101
### Provider Component
102
103
The Provider component supplies the context value to all descendant components. Components that consume the context will re-render when the Provider's value changes.
104
105
**Provider Usage Examples:**
106
107
```javascript
108
import { createElement, createContext, useState } from 'rax';
109
110
const AppContext = createContext();
111
112
function AppProvider({ children }) {
113
const [user, setUser] = useState(null);
114
const [theme, setTheme] = useState('light');
115
const [notifications, setNotifications] = useState([]);
116
117
// Context value object
118
const contextValue = {
119
user,
120
setUser,
121
theme,
122
setTheme,
123
notifications,
124
setNotifications,
125
// Helper methods
126
addNotification: (notification) => {
127
setNotifications(prev => [...prev, { ...notification, id: Date.now() }]);
128
},
129
removeNotification: (id) => {
130
setNotifications(prev => prev.filter(n => n.id !== id));
131
}
132
};
133
134
return createElement(AppContext.Provider, { value: contextValue }, children);
135
}
136
137
// Nested providers for different concerns
138
function App() {
139
return createElement(AppProvider, null,
140
createElement('div', { className: 'app' },
141
createElement(Header),
142
createElement(MainContent),
143
createElement(Footer)
144
)
145
);
146
}
147
```
148
149
### Consumer Component
150
151
The Consumer component uses a render prop pattern to access the context value. The child function receives the current context value as its argument.
152
153
**Consumer Usage Examples:**
154
155
```javascript
156
import { createElement } from 'rax';
157
158
// Using Consumer with render prop
159
function ThemedButton() {
160
return createElement(ThemeContext.Consumer, null, (themeContext) => {
161
if (!themeContext) {
162
return createElement('button', null, 'No theme available');
163
}
164
165
return createElement('button', {
166
style: {
167
backgroundColor: themeContext.theme === 'light' ? '#fff' : '#333',
168
color: themeContext.theme === 'light' ? '#333' : '#fff',
169
border: `1px solid ${themeContext.theme === 'light' ? '#ccc' : '#666'}`
170
},
171
onClick: themeContext.toggleTheme
172
}, `Switch to ${themeContext.theme === 'light' ? 'dark' : 'light'} mode`);
173
});
174
}
175
176
// Multiple context consumers
177
function UserProfile() {
178
return createElement(UserContext.Consumer, null, (userContext) =>
179
createElement(ThemeContext.Consumer, null, (themeContext) => {
180
if (!userContext.user) {
181
return createElement('div', null, 'Please log in');
182
}
183
184
return createElement('div', {
185
className: `user-profile ${themeContext.theme}`
186
},
187
createElement('h2', null, userContext.user.name),
188
createElement('p', null, `ID: ${userContext.user.id}`),
189
createElement('button', {
190
onClick: () => userContext.setUser(null)
191
}, 'Logout')
192
);
193
})
194
);
195
}
196
```
197
198
### Context with Hooks
199
200
The recommended modern approach is to use `useContext` hook instead of Consumer components for cleaner code.
201
202
**Hook-based Context Usage:**
203
204
```javascript
205
import { createElement, createContext, useContext, useState } from 'rax';
206
207
const AuthContext = createContext();
208
209
// Custom hook for easier context consumption
210
function useAuth() {
211
const context = useContext(AuthContext);
212
if (!context) {
213
throw new Error('useAuth must be used within an AuthProvider');
214
}
215
return context;
216
}
217
218
function AuthProvider({ children }) {
219
const [user, setUser] = useState(null);
220
const [loading, setLoading] = useState(false);
221
222
const login = async (email, password) => {
223
setLoading(true);
224
try {
225
const response = await fetch('/api/login', {
226
method: 'POST',
227
headers: { 'Content-Type': 'application/json' },
228
body: JSON.stringify({ email, password })
229
});
230
const userData = await response.json();
231
setUser(userData);
232
} catch (error) {
233
console.error('Login failed:', error);
234
} finally {
235
setLoading(false);
236
}
237
};
238
239
const logout = () => {
240
setUser(null);
241
};
242
243
const value = {
244
user,
245
loading,
246
login,
247
logout,
248
isAuthenticated: !!user
249
};
250
251
return createElement(AuthContext.Provider, { value }, children);
252
}
253
254
// Components using the custom hook
255
function LoginButton() {
256
const { login, loading, isAuthenticated } = useAuth();
257
258
if (isAuthenticated) {
259
return createElement('span', null, 'Already logged in');
260
}
261
262
const handleLogin = () => {
263
login('user@example.com', 'password');
264
};
265
266
return createElement('button', {
267
onClick: handleLogin,
268
disabled: loading
269
}, loading ? 'Logging in...' : 'Login');
270
}
271
272
function UserGreeting() {
273
const { user, logout } = useAuth();
274
275
if (!user) {
276
return createElement('div', null, 'Please log in');
277
}
278
279
return createElement('div', null,
280
createElement('p', null, `Welcome, ${user.name}!`),
281
createElement('button', { onClick: logout }, 'Logout')
282
);
283
}
284
```
285
286
### Context Best Practices
287
288
**Performance Optimization:**
289
290
```javascript
291
import { createElement, createContext, useContext, useMemo } from 'rax';
292
293
const AppContext = createContext();
294
295
function AppProvider({ children }) {
296
const [user, setUser] = useState(null);
297
const [theme, setTheme] = useState('light');
298
299
// Memoize context value to prevent unnecessary re-renders
300
const contextValue = useMemo(() => ({
301
user,
302
setUser,
303
theme,
304
setTheme
305
}), [user, theme]);
306
307
return createElement(AppContext.Provider, { value: contextValue }, children);
308
}
309
310
// Split contexts by concern to minimize re-renders
311
const UserContext = createContext();
312
const ThemeContext = createContext();
313
314
function UserProvider({ children }) {
315
const [user, setUser] = useState(null);
316
317
const value = useMemo(() => ({ user, setUser }), [user]);
318
319
return createElement(UserContext.Provider, { value }, children);
320
}
321
322
function ThemeProvider({ children }) {
323
const [theme, setTheme] = useState('light');
324
325
const value = useMemo(() => ({ theme, setTheme }), [theme]);
326
327
return createElement(ThemeContext.Provider, { value }, children);
328
}
329
```
330
331
## Types
332
333
```javascript { .api }
334
// Context object type
335
interface Context<T> {
336
Provider: ComponentType<ProviderProps<T>>;
337
Consumer: ComponentType<ConsumerProps<T>>;
338
_contextID: string;
339
_defaultValue: T;
340
__getNearestParentProvider: Function;
341
}
342
343
// Provider component props
344
interface ProviderProps<T> {
345
value: T;
346
children?: RaxNode;
347
}
348
349
// Consumer component props
350
interface ConsumerProps<T> {
351
children: (value: T) => RaxNode;
352
}
353
354
// Context value type helper
355
type ContextType<C extends Context<any>> = C extends Context<infer T> ? T : never;
356
357
// Custom hook return type
358
interface UseContextReturn<T> {
359
(context: Context<T>): T;
360
}
361
```