or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-hooks.mdconcurrency-helpers.mdcore-hooks.mdfamily-patterns.mdindex.mdloadable-system.mdmemory-management.mdroot-provider.mdstate-definition.md

memory-management.mddocs/

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