0
# Memory Management
1
2
Tools for managing memory usage and preventing unwanted garbage collection of Recoil state. The memory management system allows fine-grained control over when atoms and selectors are retained in memory versus cleaned up.
3
4
## Capabilities
5
6
### State Retention
7
8
Hook for preventing garbage collection of atoms, selectors, and retention zones.
9
10
```typescript { .api }
11
/**
12
* Retains Recoil state in memory, preventing garbage collection
13
* until the component unmounts or dependencies change
14
*/
15
function useRetain(
16
toRetain: RecoilValue<any> | RetentionZone | Array<RecoilValue<any> | RetentionZone>
17
): void;
18
```
19
20
**Usage Examples:**
21
22
```typescript
23
import React from 'react';
24
import { useRetain, atom, selector, atomFamily } from 'recoil';
25
26
const expensiveDataState = selector({
27
key: 'expensiveDataState',
28
get: async () => {
29
// Expensive computation or API call
30
const response = await fetch('/api/expensive-data');
31
return response.json();
32
},
33
});
34
35
// Retain single state
36
function DataPreloader() {
37
// Keep expensive data in memory even if no components are using it
38
useRetain(expensiveDataState);
39
40
return null; // This component just preloads data
41
}
42
43
// Retain multiple states
44
function CacheManager({ userIds }) {
45
const userStates = userIds.map(id => userProfileState(id));
46
47
// Keep all user profiles in memory
48
useRetain(userStates);
49
50
return <div>Caching {userIds.length} user profiles</div>;
51
}
52
53
// Conditional retention
54
function ConditionalCache({ shouldCache, dataState }) {
55
// Only retain if shouldCache is true
56
if (shouldCache) {
57
useRetain(dataState);
58
}
59
60
return <div>Cache status: {shouldCache ? 'active' : 'inactive'}</div>;
61
}
62
63
// Retain family instances
64
function FamilyCache({ activeItems }) {
65
const itemStates = activeItems.map(id => itemState(id));
66
67
// Keep active items in memory for fast access
68
useRetain(itemStates);
69
70
return <div>Retaining {activeItems.length} items</div>;
71
}
72
```
73
74
### Retention Zones
75
76
System for grouping related state for coordinated memory management.
77
78
```typescript { .api }
79
/**
80
* Creates a retention zone for coordinated memory management
81
*/
82
function retentionZone(): RetentionZone;
83
84
class RetentionZone {
85
// Internal implementation details
86
}
87
```
88
89
**Usage Examples:**
90
91
```typescript
92
import React, { useMemo } from 'react';
93
import { retentionZone, useRetain, atom, atomFamily } from 'recoil';
94
95
// Create retention zone for related data
96
function UserDataManager({ userId }) {
97
const userZone = useMemo(() => retentionZone(), [userId]);
98
99
// Retain the entire zone
100
useRetain(userZone);
101
102
// All user-related data will be retained together
103
return (
104
<div>
105
<UserProfile userId={userId} retentionZone={userZone} />
106
<UserPosts userId={userId} retentionZone={userZone} />
107
<UserSettings userId={userId} retentionZone={userZone} />
108
</div>
109
);
110
}
111
112
// Atoms that belong to a retention zone
113
const userProfileState = atomFamily({
114
key: 'userProfileState',
115
default: null,
116
effects: (userId) => [
117
({node}) => {
118
// Associate with retention zone if available
119
const zone = getCurrentRetentionZone(); // Custom context
120
if (zone) {
121
zone.retain(node);
122
}
123
},
124
],
125
});
126
127
// Page-level retention zone
128
function PageWithRetention({ page }) {
129
const pageZone = useMemo(() => retentionZone(), [page]);
130
131
// Retain all data related to this page
132
useRetain(pageZone);
133
134
return (
135
<RetentionZoneProvider zone={pageZone}>
136
<PageContent page={page} />
137
</RetentionZoneProvider>
138
);
139
}
140
141
// Custom hook for zone-aware state
142
function useZoneAwareState(stateFamily, param) {
143
const zone = useRetentionZone(); // Custom hook
144
const state = stateFamily(param);
145
146
// Automatically retain state in current zone
147
useRetain([state, zone]);
148
149
return state;
150
}
151
```
152
153
### Memory Management Patterns
154
155
Common patterns for effective memory management in Recoil applications.
156
157
**Usage Examples:**
158
159
```typescript
160
import React, { useEffect, useMemo } from 'react';
161
import { useRetain, atomFamily, selectorFamily } from 'recoil';
162
163
// LRU-style retention for frequently accessed data
164
function useLRURetention(items, maxRetained = 10) {
165
const [retainedItems, setRetainedItems] = React.useState([]);
166
167
// Update retained items when accessed items change
168
useEffect(() => {
169
const newRetained = [...new Set([...items, ...retainedItems])]
170
.slice(0, maxRetained);
171
setRetainedItems(newRetained);
172
}, [items, maxRetained]);
173
174
// Retain the LRU items
175
const statesToRetain = retainedItems.map(id => itemState(id));
176
useRetain(statesToRetain);
177
178
return retainedItems;
179
}
180
181
// Component using LRU retention
182
function ItemBrowser({ currentItems }) {
183
const retainedItems = useLRURetention(currentItems, 20);
184
185
return (
186
<div>
187
<div>Current items: {currentItems.length}</div>
188
<div>Retained in memory: {retainedItems.length}</div>
189
{currentItems.map(id => (
190
<ItemCard key={id} itemId={id} />
191
))}
192
</div>
193
);
194
}
195
196
// Preloading with retention
197
function useDataPreloader(dataKeys) {
198
const [preloadedKeys, setPreloadedKeys] = React.useState([]);
199
200
// Preload data in the background
201
useEffect(() => {
202
const preloadTimer = setTimeout(() => {
203
setPreloadedKeys(dataKeys);
204
}, 100); // Small delay to not block initial render
205
206
return () => clearTimeout(preloadTimer);
207
}, [dataKeys]);
208
209
// Retain preloaded data
210
const preloadedStates = preloadedKeys.map(key => dataState(key));
211
useRetain(preloadedStates);
212
213
return preloadedKeys;
214
}
215
216
// Route-based retention
217
function RouteDataManager({ route, subRoutes }) {
218
const routeZone = useMemo(() => retentionZone(), [route]);
219
220
// Retain main route data
221
useRetain([routeZone, routeDataState(route)]);
222
223
// Preload and retain sub-route data
224
const subRouteStates = subRoutes.map(sr => routeDataState(sr));
225
useRetain(subRouteStates);
226
227
return (
228
<div>
229
<div>Route: {route}</div>
230
<div>Sub-routes preloaded: {subRoutes.length}</div>
231
</div>
232
);
233
}
234
235
// Session-based retention
236
function useSessionRetention() {
237
const sessionZone = useMemo(() => retentionZone(), []);
238
239
// Retain for entire session
240
useRetain(sessionZone);
241
242
// Auto-retain frequently accessed data
243
const retainInSession = (state) => {
244
useRetain([state, sessionZone]);
245
return state;
246
};
247
248
return { retainInSession, sessionZone };
249
}
250
251
// Memory-conscious component
252
function MemoryEfficientList({ items, visibleRange }) {
253
const { start, end } = visibleRange;
254
const visibleItems = items.slice(start, end);
255
256
// Only retain visible items plus a small buffer
257
const bufferSize = 5;
258
const retainStart = Math.max(0, start - bufferSize);
259
const retainEnd = Math.min(items.length, end + bufferSize);
260
const itemsToRetain = items.slice(retainStart, retainEnd);
261
262
const retainedStates = itemsToRetain.map(id => itemState(id));
263
useRetain(retainedStates);
264
265
return (
266
<div>
267
<div>Visible: {visibleItems.length} items</div>
268
<div>Retained: {itemsToRetain.length} items</div>
269
{visibleItems.map(id => (
270
<ItemRow key={id} itemId={id} />
271
))}
272
</div>
273
);
274
}
275
```
276
277
### Best Practices
278
279
**When to Use Retention:**
280
281
1. **Expensive Computations**: Retain selectors with costly calculations
282
2. **Frequently Accessed Data**: Keep commonly used data in memory
283
3. **Navigation Preloading**: Retain data for likely next pages/routes
284
4. **User Session Data**: Keep user-specific data throughout session
285
5. **Master-Detail Views**: Retain master data when viewing details
286
287
**When NOT to Use Retention:**
288
289
1. **One-time Data**: Don't retain data that's only used once
290
2. **Large Datasets**: Be cautious with memory usage for large data
291
3. **Dynamic Content**: Don't retain rapidly changing data unnecessarily
292
4. **Mobile Applications**: Be more conservative on memory-constrained devices
293
294
**Usage Examples:**
295
296
```typescript
297
import React from 'react';
298
import { useRetain, useRecoilValue } from 'recoil';
299
300
// Good: Retain expensive computation used across app
301
function GlobalDataProvider() {
302
useRetain(expensiveGlobalComputationState);
303
return null;
304
}
305
306
// Good: Retain user session data
307
function UserSessionManager({ user }) {
308
useRetain([
309
userProfileState(user.id),
310
userPreferencesState(user.id),
311
userPermissionsState(user.id),
312
]);
313
return null;
314
}
315
316
// Caution: Large data retention
317
function DataTableManager({ tableId }) {
318
const tableSize = useRecoilValue(tableSizeState(tableId));
319
320
// Only retain if table is reasonably sized
321
if (tableSize < 10000) {
322
useRetain(tableDataState(tableId));
323
}
324
325
return <div>Table size: {tableSize} rows</div>;
326
}
327
328
// Good: Conditional retention based on usage patterns
329
function SmartRetention({ userId, isFrequentUser }) {
330
const userDataState = userProfileState(userId);
331
332
// Only retain for frequent users
333
if (isFrequentUser) {
334
useRetain(userDataState);
335
}
336
337
const userData = useRecoilValue(userDataState);
338
return <div>User: {userData.name}</div>;
339
}
340
```
341
342
## Performance Considerations
343
344
**Memory Usage:**
345
- Monitor memory consumption in development tools
346
- Use retention zones to group related state for easier management
347
- Consider implementing custom retention policies for large applications
348
349
**Cleanup:**
350
- Retention automatically cleans up when components unmount
351
- Be mindful of component lifecycle when using retention
352
- Use retention zones for coordinated cleanup of related state
353
354
**Testing:**
355
- Test memory usage patterns in realistic scenarios
356
- Verify that retention doesn't cause memory leaks
357
- Monitor performance impact of retention policies