0
# Component Utilities
1
2
React Hot Loader provides utility functions for component comparison, cold component marking, and hot reload control. These utilities help manage component hot reloading behavior and provide fine-grained control over the hot reload process.
3
4
## Capabilities
5
6
### Component Comparison
7
8
Tests whether two React component types are equal, taking into account proxy components used by hot reloading.
9
10
```typescript { .api }
11
/**
12
* Tests if types of two components are equal
13
* @param typeA - First component type
14
* @param typeB - Second component type
15
* @returns true if components are equal (considering hot reload proxies)
16
*/
17
function areComponentsEqual<T>(typeA: React.ComponentType<T>, typeB: React.ComponentType<T>): boolean;
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
import { areComponentsEqual } from 'react-hot-loader';
24
25
const ComponentA = () => <div>A</div>;
26
const ComponentB = () => <div>B</div>;
27
28
// Basic comparison
29
console.log(areComponentsEqual(ComponentA, ComponentA)); // true
30
console.log(areComponentsEqual(ComponentA, ComponentB)); // false
31
32
// Hot reload scenario
33
const OriginalComponent = () => <div>Original</div>;
34
// After hot reload, component reference changes but areComponentsEqual
35
// will return true if it's the same logical component
36
const hotReloadedComponent = () => <div>Updated</div>;
37
38
// This accounts for hot reload proxies
39
console.log(areComponentsEqual(OriginalComponent, hotReloadedComponent));
40
// May return true if they're the same component after hot reload
41
```
42
43
```typescript
44
// TypeScript usage with generic types
45
interface Props {
46
name: string;
47
}
48
49
const UserComponent: React.FC<Props> = ({ name }) => <div>{name}</div>;
50
const AdminComponent: React.FC<Props> = ({ name }) => <div>Admin: {name}</div>;
51
52
const isSameComponent = areComponentsEqual(UserComponent, AdminComponent);
53
console.log(isSameComponent); // false
54
```
55
56
```javascript
57
// Testing in component lifecycle
58
class MyComponent extends React.Component {
59
shouldComponentUpdate(nextProps) {
60
// Check if the component type has changed due to hot reload
61
return !areComponentsEqual(this.constructor, nextProps.componentType);
62
}
63
64
render() {
65
return <div>My Component</div>;
66
}
67
}
68
```
69
70
### Cold Component Marking
71
72
Marks a component as "cold", making it invisible to React Hot Loader. Changes to cold components will cause local state loss and full re-mounting.
73
74
```typescript { .api }
75
/**
76
* Marks component as cold, making it invisible for React-Hot-Loader
77
* Any changes to that component will cause local state loss
78
* @param component - Component to mark as cold
79
* @returns The same component (unchanged)
80
*/
81
function cold<T = React.ComponentType<any>>(component: T): T;
82
```
83
84
**Usage Examples:**
85
86
```javascript
87
import React from 'react';
88
import { cold } from 'react-hot-loader';
89
90
// Mark a component as cold
91
const StaticComponent = cold(() => {
92
return <div>This component won't hot reload</div>;
93
});
94
95
export default StaticComponent;
96
// Changes to this component will cause full re-mount, losing state
97
```
98
99
```javascript
100
// Cold class component
101
import { cold } from 'react-hot-loader';
102
103
class DatabaseConnection extends React.Component {
104
constructor(props) {
105
super(props);
106
this.connection = establishConnection();
107
}
108
109
componentWillUnmount() {
110
this.connection.close();
111
}
112
113
render() {
114
return <div>Connected to database</div>;
115
}
116
}
117
118
// Mark as cold to prevent hot reload interference with database connection
119
export default cold(DatabaseConnection);
120
```
121
122
```typescript
123
// TypeScript cold component
124
import { cold } from 'react-hot-loader';
125
126
interface CriticalProps {
127
securityToken: string;
128
}
129
130
const SecurityComponent: React.FC<CriticalProps> = ({ securityToken }) => {
131
// This component handles sensitive operations
132
return <div>Secure Area</div>;
133
};
134
135
// Mark as cold for security reasons - always full reload
136
export default cold(SecurityComponent);
137
```
138
139
```javascript
140
// Conditional cold marking
141
import { cold } from 'react-hot-loader';
142
143
const MyComponent = () => <div>My Component</div>;
144
145
// Only mark as cold in certain conditions
146
const shouldBeCold = process.env.DISABLE_HOT_RELOAD === 'true';
147
const ExportedComponent = shouldBeCold ? cold(MyComponent) : MyComponent;
148
149
export default ExportedComponent;
150
```
151
152
## Advanced Usage Patterns
153
154
### Selective Hot Reloading
155
156
```javascript
157
import { hot, cold } from 'react-hot-loader';
158
159
// Hot reload for UI components
160
const UIComponent = hot(module)(() => <div>UI Component</div>);
161
162
// Cold for critical system components
163
const SystemComponent = cold(() => <div>System Component</div>);
164
165
// Mixed usage in app
166
const App = () => (
167
<div>
168
<UIComponent /> {/* Will hot reload and preserve state */}
169
<SystemComponent /> {/* Will full reload and reset state */}
170
</div>
171
);
172
173
export default hot(module)(App);
174
```
175
176
### Component Comparison in Testing
177
178
```javascript
179
import { areComponentsEqual } from 'react-hot-loader';
180
import { render } from '@testing-library/react';
181
182
describe('Component equality', () => {
183
it('should recognize same components', () => {
184
const ComponentA = () => <div>Test</div>;
185
const ComponentB = () => <div>Test</div>;
186
187
// In testing, these are different functions
188
expect(areComponentsEqual(ComponentA, ComponentA)).toBe(true);
189
expect(areComponentsEqual(ComponentA, ComponentB)).toBe(false);
190
});
191
192
it('should handle hot reloaded components', () => {
193
// Mock hot reload scenario
194
const originalComponent = () => <div>Original</div>;
195
196
// Simulate hot reload by creating new component with same "identity"
197
const reloadedComponent = () => <div>Reloaded</div>;
198
199
// In actual hot reload, these would be considered equal
200
// Test your specific hot reload logic here
201
});
202
});
203
```
204
205
### Error Boundary with Cold Components
206
207
```javascript
208
import React from 'react';
209
import { cold } from 'react-hot-loader';
210
211
// Error boundary should be cold to prevent hot reload issues
212
class ErrorBoundary extends React.Component {
213
constructor(props) {
214
super(props);
215
this.state = { hasError: false };
216
}
217
218
static getDerivedStateFromError(error) {
219
return { hasError: true };
220
}
221
222
componentDidCatch(error, errorInfo) {
223
console.error('Error caught by boundary:', error, errorInfo);
224
}
225
226
render() {
227
if (this.state.hasError) {
228
return <div>Something went wrong.</div>;
229
}
230
231
return this.props.children;
232
}
233
}
234
235
// Mark error boundary as cold for reliability
236
export default cold(ErrorBoundary);
237
```
238
239
### Third-Party Component Integration
240
241
```javascript
242
import { cold } from 'react-hot-loader';
243
import SomeThirdPartyComponent from 'some-library';
244
245
// Third-party components often don't work well with hot reload
246
const SafeThirdPartyComponent = cold(SomeThirdPartyComponent);
247
248
// Wrapper component can still be hot
249
const MyWrapper = ({ data }) => (
250
<div>
251
<h1>My App</h1>
252
<SafeThirdPartyComponent data={data} />
253
</div>
254
);
255
256
export default hot(module)(MyWrapper);
257
```
258
259
## Performance Considerations
260
261
### When to Use Cold Components
262
263
```javascript
264
// ✅ Good candidates for cold():
265
// - Components with complex initialization
266
// - Components that manage external resources
267
// - Third-party components
268
// - Error boundaries
269
// - Components with side effects in constructor
270
271
// ❌ Avoid cold() for:
272
// - Simple presentational components
273
// - Components you frequently modify
274
// - Components where state preservation is important
275
```
276
277
### Component Comparison Performance
278
279
```javascript
280
// areComponentsEqual() is lightweight but consider caching for heavy usage
281
const componentComparison = useMemo(() => {
282
return areComponentsEqual(ComponentA, ComponentB);
283
}, [ComponentA, ComponentB]);
284
```
285
286
## Debugging Component Issues
287
288
```javascript
289
import { areComponentsEqual, cold } from 'react-hot-loader';
290
291
const DebugComponent = ({ children }) => {
292
useEffect(() => {
293
// Debug component equality issues
294
console.log('Component equality check:',
295
areComponentsEqual(children.type, children.type)
296
);
297
}, [children]);
298
299
return children;
300
};
301
302
// Use cold for debugging to avoid hot reload interference
303
export default cold(DebugComponent);
304
```
305
306
## Migration Notes
307
308
### Legacy Component Comparison
309
310
```javascript
311
// Old way (manual proxy checking)
312
const getActualComponent = (component) => {
313
return component.__hotReloadProxy ?
314
component.__hotReloadProxy.getCurrent() :
315
component;
316
};
317
318
const manualComparison = (a, b) => {
319
return getActualComponent(a) === getActualComponent(b);
320
};
321
322
// New way (recommended)
323
import { areComponentsEqual } from 'react-hot-loader';
324
const modernComparison = areComponentsEqual(a, b);
325
```
326
327
### Component Configuration
328
329
Sets specific options for individual components, providing fine-grained control over hot reload behavior for specific components.
330
331
```typescript { .api }
332
/**
333
* Configure options for a specific component
334
* @param component - Component to configure
335
* @param options - Configuration options for the component
336
* @returns Configured component
337
*/
338
function configureComponent(component: React.ComponentType<any>, options: any): React.ComponentType<any>;
339
```
340
341
**Usage Examples:**
342
343
```javascript
344
import { configureComponent } from 'react-hot-loader';
345
346
const MyComponent = () => <div>My Component</div>;
347
348
// Configure component with specific options
349
const ConfiguredComponent = configureComponent(MyComponent, {
350
pureSFC: true,
351
pureRender: false
352
});
353
354
export default ConfiguredComponent;
355
```
356
357
```javascript
358
// Configure multiple components differently
359
const FastComponent = configureComponent(ComponentA, {
360
pureSFC: true,
361
pureRender: true
362
});
363
364
const RichComponent = configureComponent(ComponentB, {
365
pureSFC: false,
366
allowSFC: true
367
});
368
```
369
370
### Cold Component Best Practices
371
372
```javascript
373
// ✅ Good: Export cold components directly
374
export default cold(MyComponent);
375
376
// ✅ Good: Conditional cold based on environment
377
export default process.env.NODE_ENV === 'test'
378
? cold(MyComponent)
379
: hot(module)(MyComponent);
380
381
// ❌ Avoid: Mixing hot and cold on same component
382
const Component = hot(module)(cold(MyComponent)); // Don't do this
383
384
// ❌ Avoid: Cold inside render methods
385
const BadExample = () => {
386
const ColdComponent = cold(SomeComponent); // Don't do this
387
return <ColdComponent />;
388
};
389
```