0
# App Container
1
2
The `AppContainer` component is an error boundary that manages the hot reload lifecycle and provides error handling during development. It wraps React components to catch and display errors without crashing the entire application.
3
4
## Capabilities
5
6
### AppContainer Component
7
8
React component that serves as an error boundary and container for hot-reloadable components.
9
10
```typescript { .api }
11
/**
12
* Error boundary component for hot reload management
13
* Catches errors during development and provides error recovery
14
*/
15
class AppContainer extends React.Component<AppContainerProps & AppChildren> {
16
constructor(props: AppContainerProps & AppChildren);
17
18
/** Catches errors during component rendering */
19
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
20
21
/** Renders children with error boundary protection */
22
render(): React.ReactElement;
23
24
/** Retries after a hot loader error */
25
retryHotLoaderError(): void;
26
}
27
28
interface AppContainerProps {
29
/** Enable/disable error boundary functionality (default: true) */
30
errorBoundary?: boolean;
31
/** Custom error reporter component */
32
errorReporter?: React.ComponentType<ErrorReporterProps>;
33
}
34
35
interface AppChildren {
36
/** Single React element to wrap (validated) */
37
children?: React.ReactElement<any>;
38
}
39
40
interface ErrorReporterProps {
41
error: any;
42
errorInfo?: React.ErrorInfo;
43
component?: AppContainer;
44
}
45
```
46
47
**Usage Examples:**
48
49
```javascript
50
// Basic usage (legacy pattern)
51
import React from 'react';
52
import ReactDOM from 'react-dom';
53
import { AppContainer } from 'react-hot-loader';
54
import App from './App';
55
56
const render = (Component) => {
57
ReactDOM.render(
58
<AppContainer>
59
<Component />
60
</AppContainer>,
61
document.getElementById('root')
62
);
63
};
64
65
render(App);
66
67
// Enable hot reloading
68
if (module.hot) {
69
module.hot.accept('./App', () => {
70
render(App);
71
});
72
}
73
```
74
75
```javascript
76
// With custom error reporter
77
import { AppContainer } from 'react-hot-loader';
78
79
const CustomErrorDisplay = ({ error, errorInfo, component }) => (
80
<div className="error-boundary">
81
<h1>Development Error</h1>
82
<p>{error.message}</p>
83
<button onClick={() => component.retryHotLoaderError()}>
84
Try Again
85
</button>
86
</div>
87
);
88
89
const Root = () => (
90
<AppContainer
91
errorReporter={CustomErrorDisplay}
92
errorBoundary={true}
93
>
94
<App />
95
</AppContainer>
96
);
97
```
98
99
```javascript
100
// Disabled error boundary
101
import { AppContainer } from 'react-hot-loader';
102
103
const Root = () => (
104
<AppContainer errorBoundary={false}>
105
<App />
106
</AppContainer>
107
);
108
```
109
110
### Error Handling
111
112
AppContainer provides comprehensive error handling during development:
113
114
```javascript
115
// Automatic error catching
116
const ProblematicComponent = () => {
117
// This error will be caught by AppContainer
118
throw new Error('Something went wrong!');
119
return <div>This won't render</div>;
120
};
121
122
const App = () => (
123
<AppContainer>
124
<ProblematicComponent />
125
</AppContainer>
126
);
127
// Error display instead of app crash
128
```
129
130
### Error Recovery
131
132
Built-in error recovery mechanism for hot reload errors:
133
134
```javascript
135
const ErrorReporter = ({ error, errorInfo, component }) => (
136
<div className="hot-reload-error">
137
<h2>Hot Reload Error</h2>
138
<details>
139
<summary>Error Details</summary>
140
<pre>{error.stack}</pre>
141
<pre>{JSON.stringify(errorInfo, null, 2)}</pre>
142
</details>
143
144
{/* Retry button triggers error recovery */}
145
<button onClick={() => component.retryHotLoaderError()}>
146
Retry Hot Reload
147
</button>
148
</div>
149
);
150
```
151
152
## Hot Reload Integration
153
154
### State Management
155
AppContainer manages hot reload state internally:
156
157
```javascript
158
// Internal state tracking (not directly accessible)
159
state = {
160
error: null,
161
errorInfo: null,
162
generation: 0 // Hot reload generation tracking
163
}
164
```
165
166
### Lifecycle Integration
167
```javascript
168
// AppContainer integrates with React lifecycle
169
static getDerivedStateFromProps(nextProps, prevState) {
170
// Resets error state on hot reload
171
if (prevState.generation !== getGeneration()) {
172
return {
173
error: null,
174
generation: getGeneration(),
175
};
176
}
177
return null;
178
}
179
180
shouldComponentUpdate(prevProps, prevState) {
181
// Prevents infinite error loops
182
if (prevState.error && this.state.error) {
183
return false;
184
}
185
return true;
186
}
187
```
188
189
## Production Behavior
190
191
In production builds, AppContainer becomes a simple pass-through component:
192
193
```javascript
194
// Production AppContainer (simplified)
195
function AppContainer(props) {
196
return React.Children.only(props.children);
197
}
198
```
199
200
## Advanced Usage
201
202
### Custom Error Reporters
203
204
```javascript
205
import { AppContainer } from 'react-hot-loader';
206
207
// Full-featured error reporter
208
const AdvancedErrorReporter = ({ error, errorInfo, component }) => {
209
const [expanded, setExpanded] = React.useState(false);
210
211
return (
212
<div style={{
213
padding: '20px',
214
backgroundColor: '#ffe6e6',
215
border: '1px solid #ff9999'
216
}}>
217
<h2>π₯ Hot Reload Error</h2>
218
<p><strong>Error:</strong> {error.message}</p>
219
220
<button onClick={() => setExpanded(!expanded)}>
221
{expanded ? 'Hide' : 'Show'} Details
222
</button>
223
224
{expanded && (
225
<div>
226
<h3>Stack Trace:</h3>
227
<pre style={{ overflow: 'auto', maxHeight: '200px' }}>
228
{error.stack}
229
</pre>
230
231
<h3>Component Stack:</h3>
232
<pre style={{ overflow: 'auto', maxHeight: '200px' }}>
233
{errorInfo.componentStack}
234
</pre>
235
</div>
236
)}
237
238
<div style={{ marginTop: '10px' }}>
239
<button onClick={() => component.retryHotLoaderError()}>
240
π Retry
241
</button>
242
<button onClick={() => window.location.reload()}>
243
π Reload Page
244
</button>
245
</div>
246
</div>
247
);
248
};
249
250
// Usage with custom reporter
251
<AppContainer errorReporter={AdvancedErrorReporter}>
252
<App />
253
</AppContainer>
254
```
255
256
### Multiple Children Validation
257
258
AppContainer enforces single child requirement:
259
260
```javascript
261
// β Valid: Single child
262
<AppContainer>
263
<App />
264
</AppContainer>
265
266
// β Invalid: Multiple children (throws error)
267
<AppContainer>
268
<Header />
269
<Main />
270
<Footer />
271
</AppContainer>
272
273
// β Valid: Single wrapper element
274
<AppContainer>
275
<div>
276
<Header />
277
<Main />
278
<Footer />
279
</div>
280
</AppContainer>
281
```
282
283
## Migration Notes
284
285
### Modern vs Legacy Usage
286
287
```javascript
288
// β Legacy pattern (still works)
289
import { AppContainer } from 'react-hot-loader';
290
291
const render = (Component) => {
292
ReactDOM.render(
293
<AppContainer>
294
<Component />
295
</AppContainer>,
296
document.getElementById('root')
297
);
298
};
299
300
// β Modern pattern (recommended)
301
import { hot } from 'react-hot-loader/root';
302
303
const App = () => <div>My App</div>;
304
305
export default hot(App);
306
// AppContainer is included automatically
307
```
308
309
### Error Boundary Best Practices
310
311
```javascript
312
// β Good: Let AppContainer handle hot reload errors
313
<AppContainer errorBoundary={true}>
314
<App />
315
</AppContainer>
316
317
// β οΈ Caution: Custom error boundaries might interfere
318
class MyErrorBoundary extends React.Component {
319
// Might catch errors that AppContainer should handle
320
}
321
322
// β Better: Disable AppContainer error boundary if using custom
323
<AppContainer errorBoundary={false}>
324
<MyErrorBoundary>
325
<App />
326
</MyErrorBoundary>
327
</AppContainer>
328
```