or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

act-utilities.mdasync-testing.mdcleanup-management.mderror-handling.mdhook-rendering.mdindex.mdserver-side-rendering.md

cleanup-management.mddocs/

0

# Cleanup Management

1

2

Cleanup utilities for managing test teardown, both automatic and manual cleanup of rendered hooks and associated resources. Proper cleanup prevents memory leaks and ensures test isolation.

3

4

## Capabilities

5

6

### cleanup Function

7

8

Runs all registered cleanup callbacks and clears the cleanup registry. This is typically called automatically after each test, but can be called manually when needed.

9

10

```typescript { .api }

11

/**

12

* Run all registered cleanup callbacks and clear the registry

13

* @returns Promise that resolves when all cleanup is complete

14

*/

15

function cleanup(): Promise<void>;

16

```

17

18

**Usage Examples:**

19

20

```typescript

21

import { renderHook, cleanup } from "@testing-library/react-hooks";

22

23

function useTimer() {

24

const [count, setCount] = useState(0);

25

26

useEffect(() => {

27

const interval = setInterval(() => {

28

setCount(prev => prev + 1);

29

}, 1000);

30

31

return () => clearInterval(interval);

32

}, []);

33

34

return count;

35

}

36

37

test("manual cleanup", async () => {

38

const { result } = renderHook(() => useTimer());

39

40

expect(result.current).toBe(0);

41

42

// Manually trigger cleanup

43

await cleanup();

44

45

// Hook should be unmounted and timers cleared

46

});

47

48

// Multiple hooks in same test

49

test("cleanup multiple hooks", async () => {

50

const { result: result1 } = renderHook(() => useTimer());

51

const { result: result2 } = renderHook(() => useTimer());

52

53

expect(result1.current).toBe(0);

54

expect(result2.current).toBe(0);

55

56

// Cleanup all rendered hooks

57

await cleanup();

58

});

59

```

60

61

### addCleanup Function

62

63

Registers a custom cleanup callback that will be called during cleanup. Returns a function to remove the callback.

64

65

```typescript { .api }

66

/**

67

* Add a custom cleanup callback

68

* @param callback - Function to call during cleanup (can be async)

69

* @returns Function to remove this cleanup callback

70

*/

71

function addCleanup(callback: CleanupCallback): () => void;

72

73

type CleanupCallback = () => Promise<void> | void;

74

```

75

76

**Usage Examples:**

77

78

```typescript

79

import { renderHook, addCleanup } from "@testing-library/react-hooks";

80

81

// Custom resource cleanup

82

test("custom cleanup callback", async () => {

83

const mockResource = {

84

data: "important data",

85

dispose: jest.fn()

86

};

87

88

// Register custom cleanup

89

const removeCleanup = addCleanup(() => {

90

mockResource.dispose();

91

mockResource.data = null;

92

});

93

94

const { result } = renderHook(() => {

95

// Hook that uses the resource

96

return mockResource.data;

97

});

98

99

expect(result.current).toBe("important data");

100

101

// Cleanup will be called automatically after test

102

// or manually with cleanup()

103

});

104

105

// Async cleanup

106

test("async cleanup callback", async () => {

107

const mockDatabase = {

108

connected: true,

109

disconnect: jest.fn().mockResolvedValue(undefined)

110

};

111

112

addCleanup(async () => {

113

await mockDatabase.disconnect();

114

mockDatabase.connected = false;

115

});

116

117

const { result } = renderHook(() => mockDatabase.connected);

118

119

expect(result.current).toBe(true);

120

});

121

122

// Conditional cleanup removal

123

test("remove cleanup callback", () => {

124

const cleanupFn = jest.fn();

125

126

const removeCleanup = addCleanup(cleanupFn);

127

128

// Later, if cleanup is no longer needed

129

removeCleanup();

130

131

// cleanupFn will not be called during cleanup

132

});

133

```

134

135

### removeCleanup Function

136

137

Removes a previously registered cleanup callback from the cleanup registry.

138

139

```typescript { .api }

140

/**

141

* Remove a previously registered cleanup callback

142

* @param callback - The cleanup callback to remove

143

*/

144

function removeCleanup(callback: CleanupCallback): void;

145

```

146

147

**Usage Examples:**

148

149

```typescript

150

import { renderHook, addCleanup, removeCleanup } from "@testing-library/react-hooks";

151

152

test("manual cleanup removal", () => {

153

const cleanupCallback = jest.fn();

154

155

// Add cleanup

156

addCleanup(cleanupCallback);

157

158

// Later, remove it

159

removeCleanup(cleanupCallback);

160

161

// Callback will not be called during cleanup

162

});

163

164

// Cleanup lifecycle management

165

function useResourceWithCleanup(shouldCleanup: boolean) {

166

const [resource] = useState(() => createResource());

167

168

useEffect(() => {

169

if (shouldCleanup) {

170

const cleanup = () => resource.dispose();

171

addCleanup(cleanup);

172

173

return () => removeCleanup(cleanup);

174

}

175

}, [shouldCleanup, resource]);

176

177

return resource;

178

}

179

180

test("conditional cleanup registration", () => {

181

const { rerender } = renderHook(

182

({ shouldCleanup }) => useResourceWithCleanup(shouldCleanup),

183

{ initialProps: { shouldCleanup: false } }

184

);

185

186

// Initially no cleanup registered

187

188

// Enable cleanup

189

rerender({ shouldCleanup: true });

190

191

// Cleanup is now registered

192

193

// Disable cleanup

194

rerender({ shouldCleanup: false });

195

196

// Cleanup is removed

197

});

198

```

199

200

### Automatic Cleanup

201

202

The library automatically registers cleanup for all rendered hooks. This happens through the auto-cleanup system:

203

204

```typescript

205

// Automatic cleanup is enabled by default

206

import { renderHook } from "@testing-library/react-hooks";

207

208

// Each renderHook call automatically registers cleanup

209

test("automatic cleanup", () => {

210

const { result, unmount } = renderHook(() => useState(0));

211

212

// Hook will be automatically cleaned up after test

213

// No manual cleanup needed

214

});

215

216

// Hooks are also cleaned up when explicitly unmounted

217

test("explicit unmount", () => {

218

const { result, unmount } = renderHook(() => useState(0));

219

220

expect(result.current[0]).toBe(0);

221

222

// Explicitly unmount (also removes from cleanup registry)

223

unmount();

224

225

// Hook is now unmounted and cleaned up

226

});

227

```

228

229

### Disabling Auto-Cleanup

230

231

You can disable automatic cleanup by importing a special configuration file:

232

233

```javascript

234

// At the top of your test file or in setup

235

import "@testing-library/react-hooks/dont-cleanup-after-each";

236

237

// Or require in CommonJS

238

require("@testing-library/react-hooks/dont-cleanup-after-each");

239

```

240

241

**Usage with Manual Cleanup:**

242

243

```typescript

244

// After importing dont-cleanup-after-each

245

import { renderHook, cleanup } from "@testing-library/react-hooks";

246

247

describe("manual cleanup tests", () => {

248

afterEach(async () => {

249

// Manually call cleanup after each test

250

await cleanup();

251

});

252

253

test("hook test 1", () => {

254

const { result } = renderHook(() => useState(0));

255

// Test logic...

256

});

257

258

test("hook test 2", () => {

259

const { result } = renderHook(() => useState(1));

260

// Test logic...

261

});

262

});

263

```

264

265

### Cleanup Error Handling

266

267

Cleanup callbacks can handle errors gracefully:

268

269

```typescript

270

test("cleanup error handling", async () => {

271

const failingCleanup = jest.fn(() => {

272

throw new Error("Cleanup failed");

273

});

274

275

const successfulCleanup = jest.fn();

276

277

addCleanup(failingCleanup);

278

addCleanup(successfulCleanup);

279

280

// Cleanup continues even if some callbacks fail

281

await cleanup();

282

283

expect(failingCleanup).toHaveBeenCalled();

284

expect(successfulCleanup).toHaveBeenCalled();

285

});

286

287

// Async cleanup errors

288

test("async cleanup error handling", async () => {

289

const failingAsyncCleanup = jest.fn().mockRejectedValue(

290

new Error("Async cleanup failed")

291

);

292

293

const successfulCleanup = jest.fn();

294

295

addCleanup(failingAsyncCleanup);

296

addCleanup(successfulCleanup);

297

298

// All cleanup callbacks run despite errors

299

await cleanup();

300

301

expect(failingAsyncCleanup).toHaveBeenCalled();

302

expect(successfulCleanup).toHaveBeenCalled();

303

});

304

```

305

306

### Best Practices

307

308

**Resource Management:**

309

```typescript

310

function useFileResource(filename: string) {

311

const [file, setFile] = useState(null);

312

313

useEffect(() => {

314

const fileHandle = openFile(filename);

315

setFile(fileHandle);

316

317

// Register cleanup for the file handle

318

const cleanup = () => fileHandle.close();

319

addCleanup(cleanup);

320

321

return () => {

322

fileHandle.close();

323

removeCleanup(cleanup);

324

};

325

}, [filename]);

326

327

return file;

328

}

329

```

330

331

**Test Isolation:**

332

```typescript

333

describe("test suite with shared resources", () => {

334

let sharedResource;

335

336

beforeEach(() => {

337

sharedResource = createSharedResource();

338

339

// Register cleanup for shared resource

340

addCleanup(() => {

341

sharedResource.dispose();

342

sharedResource = null;

343

});

344

});

345

346

test("test 1", () => {

347

const { result } = renderHook(() => useSharedResource(sharedResource));

348

// Test logic...

349

});

350

351

test("test 2", () => {

352

const { result } = renderHook(() => useSharedResource(sharedResource));

353

// Test logic...

354

});

355

});

356

```