0
# Context Integration
1
2
React Context and Higher-Order Component utilities for integrating with the live editing system. These tools allow you to build custom components that interact with the live editing state and create advanced integrations.
3
4
## Capabilities
5
6
### LiveContext
7
8
React Context that provides access to the live editing state and control functions. This context is used internally by all live components and can be consumed by custom components to access editing state.
9
10
```typescript { .api }
11
/**
12
* React Context providing live editing state and controls
13
*/
14
const LiveContext: React.Context<{
15
/** Current error message from compilation or execution */
16
error?: string;
17
/** Currently rendered React component from code execution */
18
element?: ComponentType | null;
19
/** Original code string passed to LiveProvider */
20
code: string;
21
/** Most recent code change (may differ from 'code' during editing) */
22
newCode?: string;
23
/** Whether editing is currently disabled */
24
disabled: boolean;
25
/** Language identifier for syntax highlighting */
26
language: string;
27
/** Prism theme for syntax highlighting */
28
theme?: typeof themes.nightOwl;
29
/** Function to handle and display errors */
30
onError(error: Error): void;
31
/** Function called when code content changes */
32
onChange(value: string): void;
33
}>;
34
```
35
36
**Usage Examples:**
37
38
```typescript
39
import React, { useContext } from "react";
40
import { LiveProvider, LiveContext } from "react-live";
41
42
// Custom component consuming LiveContext
43
function CodeStats() {
44
const { code, error, element } = useContext(LiveContext);
45
46
return (
47
<div className="code-stats">
48
<p>Lines: {code.split('\n').length}</p>
49
<p>Characters: {code.length}</p>
50
<p>Status: {error ? 'Error' : element ? 'Valid' : 'Empty'}</p>
51
{error && <p className="error">Error: {error}</p>}
52
</div>
53
);
54
}
55
56
// Usage within LiveProvider
57
function PlaygroundWithStats() {
58
return (
59
<LiveProvider code="<h1>Hello World</h1>">
60
<div className="editor-panel">
61
<LiveEditor />
62
<CodeStats />
63
</div>
64
<LivePreview />
65
</LiveProvider>
66
);
67
}
68
69
// Custom reset button
70
function ResetButton({ defaultCode }: { defaultCode: string }) {
71
const { onChange } = useContext(LiveContext);
72
73
return (
74
<button onClick={() => onChange(defaultCode)}>
75
Reset Code
76
</button>
77
);
78
}
79
80
// Code formatter component
81
function FormatButton() {
82
const { code, onChange } = useContext(LiveContext);
83
84
const formatCode = () => {
85
try {
86
// Simple formatter (in real app, use prettier or similar)
87
const formatted = code
88
.split('\n')
89
.map(line => line.trim())
90
.join('\n');
91
onChange(formatted);
92
} catch (error) {
93
console.error('Format failed:', error);
94
}
95
};
96
97
return (
98
<button onClick={formatCode}>
99
Format Code
100
</button>
101
);
102
}
103
104
// Custom error display with details
105
function DetailedError() {
106
const { error, code } = useContext(LiveContext);
107
108
if (!error) return null;
109
110
const getErrorDetails = (error: string) => {
111
if (error.includes('SyntaxError')) {
112
return 'Check for missing brackets, quotes, or semicolons';
113
}
114
if (error.includes('ReferenceError')) {
115
return 'Variable or component not found in scope';
116
}
117
return 'Unexpected error occurred';
118
};
119
120
return (
121
<div className="detailed-error">
122
<h4>Error Details</h4>
123
<pre className="error-message">{error}</pre>
124
<p className="error-hint">{getErrorDetails(error)}</p>
125
<details>
126
<summary>Code Context</summary>
127
<pre>{code}</pre>
128
</details>
129
</div>
130
);
131
}
132
133
// Complete custom playground
134
function CustomPlayground() {
135
const initialCode = `
136
function Welcome({ name }) {
137
return <h1>Hello, {name}!</h1>;
138
}
139
140
<Welcome name="React Live" />
141
`;
142
143
return (
144
<LiveProvider code={initialCode} noInline>
145
<div className="playground-layout">
146
<div className="editor-section">
147
<div className="editor-header">
148
<CodeStats />
149
<ResetButton defaultCode={initialCode} />
150
<FormatButton />
151
</div>
152
<LiveEditor />
153
</div>
154
<div className="preview-section">
155
<LivePreview />
156
<DetailedError />
157
</div>
158
</div>
159
</LiveProvider>
160
);
161
}
162
```
163
164
### withLive
165
166
Higher-Order Component that injects the entire LiveContext as props into a wrapped component. Useful for class components or when you need to pass live state as props.
167
168
```typescript { .api }
169
/**
170
* Higher-Order Component that injects live context as props
171
* @param WrappedComponent - Component to receive live context as props
172
* @returns Enhanced component with live context injected as 'live' prop
173
*/
174
function withLive<T>(
175
WrappedComponent: ComponentType<T & { live: Record<string, unknown> }>
176
): ComponentType<T>;
177
```
178
179
**Usage Examples:**
180
181
```typescript
182
import React, { Component } from "react";
183
import { LiveProvider, withLive } from "react-live";
184
185
// Class component with live context
186
class CodeAnalyzer extends Component<{ live: any }> {
187
analyzeProp() {
188
const { live } = this.props;
189
return {
190
hasError: !!live.error,
191
codeLength: live.code.length,
192
lineCount: live.code.split('\n').length,
193
isDisabled: live.disabled
194
};
195
}
196
197
render() {
198
const analysis = this.analyzeProp();
199
const { live } = this.props;
200
201
return (
202
<div className="code-analyzer">
203
<h3>Code Analysis</h3>
204
<ul>
205
<li>Status: {analysis.hasError ? 'Error' : 'Valid'}</li>
206
<li>Length: {analysis.codeLength} characters</li>
207
<li>Lines: {analysis.lineCount}</li>
208
<li>Editable: {analysis.isDisabled ? 'No' : 'Yes'}</li>
209
</ul>
210
{live.error && (
211
<div className="error-section">
212
<strong>Error:</strong> {live.error}
213
</div>
214
)}
215
</div>
216
);
217
}
218
}
219
220
// Enhance with live context
221
const EnhancedCodeAnalyzer = withLive(CodeAnalyzer);
222
223
// Function component with live context as props
224
function CodeToolbar({ live, onSave }: { live: any; onSave: (code: string) => void }) {
225
const handleSave = () => {
226
if (!live.error && live.code.trim()) {
227
onSave(live.code);
228
}
229
};
230
231
const handleClear = () => {
232
live.onChange('');
233
};
234
235
return (
236
<div className="code-toolbar">
237
<button
238
onClick={handleSave}
239
disabled={!!live.error || !live.code.trim()}
240
>
241
Save Code
242
</button>
243
<button onClick={handleClear}>
244
Clear
245
</button>
246
<span className="status">
247
{live.error ? '❌ Error' : '✅ Valid'}
248
</span>
249
</div>
250
);
251
}
252
253
const EnhancedCodeToolbar = withLive(CodeToolbar);
254
255
// Usage in application
256
function AppWithLiveComponents() {
257
const [savedCodes, setSavedCodes] = useState<string[]>([]);
258
259
const handleSave = (code: string) => {
260
setSavedCodes(prev => [...prev, code]);
261
};
262
263
return (
264
<div className="app">
265
<LiveProvider code="<h1>Start coding...</h1>">
266
<div className="main-editor">
267
<EnhancedCodeToolbar onSave={handleSave} />
268
<LiveEditor />
269
<EnhancedCodeAnalyzer />
270
</div>
271
<div className="preview-area">
272
<LivePreview />
273
<LiveError />
274
</div>
275
</LiveProvider>
276
277
<div className="saved-codes">
278
<h3>Saved Codes ({savedCodes.length})</h3>
279
{savedCodes.map((code, index) => (
280
<details key={index}>
281
<summary>Code #{index + 1}</summary>
282
<pre>{code}</pre>
283
</details>
284
))}
285
</div>
286
</div>
287
);
288
}
289
290
// Custom hook alternative to withLive HOC
291
function useLiveContext() {
292
const context = useContext(LiveContext);
293
if (!context) {
294
throw new Error('useLiveContext must be used within a LiveProvider');
295
}
296
return context;
297
}
298
299
// Usage with custom hook
300
function ModernCodeAnalyzer() {
301
const live = useLiveContext();
302
303
const stats = useMemo(() => ({
304
words: live.code.split(/\s+/).filter(Boolean).length,
305
jsx: live.code.includes('<') && live.code.includes('>'),
306
typescript: live.code.includes(':') && live.enableTypeScript,
307
hasHooks: /use[A-Z]/.test(live.code)
308
}), [live.code]);
309
310
return (
311
<div className="modern-analyzer">
312
<h4>Code Statistics</h4>
313
<div className="stats-grid">
314
<div>Words: {stats.words}</div>
315
<div>JSX: {stats.jsx ? '✓' : '✗'}</div>
316
<div>TypeScript: {stats.typescript ? '✓' : '✗'}</div>
317
<div>Hooks: {stats.hasHooks ? '✓' : '✗'}</div>
318
</div>
319
</div>
320
);
321
}
322
```
323
324
## Advanced Integration Patterns
325
326
### State Synchronization
327
328
```typescript
329
// Synchronize external state with live context
330
function useSyncedLiveCode(externalCode: string) {
331
const { code, onChange } = useContext(LiveContext);
332
333
useEffect(() => {
334
if (code !== externalCode) {
335
onChange(externalCode);
336
}
337
}, [externalCode, code, onChange]);
338
339
return { code, onChange };
340
}
341
342
// Bidirectional sync
343
function useBidirectionalSync(
344
externalCode: string,
345
onExternalChange: (code: string) => void
346
) {
347
const { code, onChange } = useContext(LiveContext);
348
349
// Sync external -> live
350
useEffect(() => {
351
if (code !== externalCode) {
352
onChange(externalCode);
353
}
354
}, [externalCode]);
355
356
// Sync live -> external (debounced)
357
useEffect(() => {
358
const timer = setTimeout(() => {
359
if (code !== externalCode) {
360
onExternalChange(code);
361
}
362
}, 1000);
363
364
return () => clearTimeout(timer);
365
}, [code, externalCode, onExternalChange]);
366
}
367
```
368
369
### Error Recovery
370
371
```typescript
372
function AutoRecoveringEditor() {
373
const { code, error, onChange } = useContext(LiveContext);
374
const [lastValidCode, setLastValidCode] = useState(code);
375
376
useEffect(() => {
377
if (!error && code.trim()) {
378
setLastValidCode(code);
379
}
380
}, [code, error]);
381
382
const recoverToLastValid = () => {
383
onChange(lastValidCode);
384
};
385
386
return (
387
<div className="recovering-editor">
388
<LiveEditor />
389
{error && (
390
<div className="error-recovery">
391
<p>Error detected: {error}</p>
392
<button onClick={recoverToLastValid}>
393
Recover to Last Valid Code
394
</button>
395
</div>
396
)}
397
</div>
398
);
399
}
400
```
401
402
### Performance Optimization
403
404
```typescript
405
// Memoized context consumer
406
const MemoizedCodeDisplay = React.memo(() => {
407
const { code, error } = useContext(LiveContext);
408
409
return (
410
<div className="code-display">
411
<pre>{code}</pre>
412
{error && <div className="error">{error}</div>}
413
</div>
414
);
415
});
416
417
// Debounced change handler
418
function useDebouncedLiveChange(delay = 300) {
419
const { onChange } = useContext(LiveContext);
420
const debouncedOnChange = useMemo(
421
() => debounce(onChange, delay),
422
[onChange, delay]
423
);
424
425
return debouncedOnChange;
426
}
427
```