Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8d0fc82

Browse files
committed
Refactor addCleanup to not treat unmount differently for external cleanups
1 parent 01918d9 commit 8d0fc82

File tree

5 files changed

+128
-58
lines changed

5 files changed

+128
-58
lines changed

‎docs/api-reference.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ This is the same [`act` function](https://reactjs.org/docs/test-utils.html#act)
109109
function cleanup: Promise<void>
110110
```
111111

112-
Unmounts any rendered hooks rendered with `renderHook`, ensuring all effects have been flushed.
112+
Unmounts any rendered hooks rendered with `renderHook`, ensuring all effects have been flushed. Any
113+
callbacks added with [`addCleanup`](<(/reference/api#addCleanup).>) will also be called when
114+
`cleanup` is run.
113115

114116
> Please note that this is done automatically if the testing framework you're using supports the
115117
> `afterEach` global (like Jest, mocha and Jasmine). If not, you will need to do manual cleanups
@@ -151,26 +153,30 @@ variable to `true` before importing `@testing-library/react-hooks` will also dis
151153
## `addCleanup`
152154
153155
```js
154-
function addCleanup(
155-
callback: function(props?: any): any
156-
): void
156+
function addCleanup(callback: function(): void|Promise<void>): function(): void
157157
```
158158

159-
Callback to be called after `cleanup`.
159+
Add a callback to be called during [`cleanup`](/reference/api#cleanup), returning a function to
160+
remove the cleanup if is no longer required. Cleanups are called in reverse order to being added.
161+
This is usually only relevant when wanting a cleanup to run after the component has been unmounted.
160162

161-
In some cases you might want to run some callback after internal `cleanup` happen, especially after
162-
`unmount` happens in `cleanup`. If the sequence matters to you, you could use `addCleanup`.
163+
If the provided callback is an `async` function or returns a promise, `cleanup` will wait for it to
164+
be resolved before moving onto the next cleanup callback.
163165

164-
```js
165-
import { addCleanup } from '@testing-library/react-hooks'
166+
> Please note that any cleanups added using `addCleanup` are removed after `cleanup` is called. For
167+
> cleanups that need to run with every test, it is advised to add them in a `beforeEach` block (or
168+
> equivalent for your test runner).
166169

167-
jest.useFakeTimers()
170+
## `removeCleanup`
168171

169-
addCleanup(() => {
170-
jest.runOnlyPendingTimers()
171-
})
172+
```js
173+
function removeCleanup(callback: function(): void|Promise<void>): void
172174
```
173175

176+
Removes a cleanup callback previously added with [`addCleanup`](/reference/api#addCleanup). Once
177+
removed, the provided callback will no longer execute as part of running
178+
[`cleanup`](/reference/api#cleanup).
179+
174180
---
175181

176182
## Async Utilities

‎src/cleanup.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
import flushMicroTasks from './flush-microtasks'
22

3-
let internalCleanupCbs = []
4-
let cleanupCbs = []
3+
let cleanupCallbacks = []
54

65
async function cleanup() {
76
await flushMicroTasks()
8-
internalCleanupCbs.forEach((cb) => cb())
9-
internalCleanupCbs = []
10-
cleanupCbs.forEach((cb) => cb())
11-
}
12-
13-
function addInternalCleanup(callback) {
14-
internalCleanupCbs.push(callback)
7+
for (const callback of cleanupCallbacks) {
8+
await callback()
9+
}
10+
cleanupCallbacks = []
1511
}
1612

1713
function addCleanup(callback) {
18-
cleanupCbs.push(callback)
14+
cleanupCallbacks.unshift(callback)
15+
return () => removeCleanup(callback)
1916
}
2017

21-
function removeInternalCleanup(callback) {
22-
internalCleanupCbs = internalCleanupCbs.filter((cb) => cb !== callback)
18+
function removeCleanup(callback) {
19+
cleanupCallbacks = cleanupCallbacks.filter((cb) => cb !== callback)
2320
}
2421

25-
export { cleanup, addCleanup, addInternalCleanup,removeInternalCleanup }
22+
export { cleanup, addCleanup, removeCleanup }

‎src/pure.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Suspense } from 'react'
22
import { act, create } from 'react-test-renderer'
33
import asyncUtils from './asyncUtils'
4-
import { cleanup, addCleanup, addInternalCleanup,removeInternalCleanup } from './cleanup'
4+
import { cleanup, addCleanup, removeCleanup } from './cleanup'
55

66
function TestHook({ callback, hookProps, onError, children }) {
77
try {
@@ -84,12 +84,12 @@ function renderHook(callback, { initialProps, wrapper } = {}) {
8484

8585
function unmountHook() {
8686
act(() => {
87-
removeInternalCleanup(unmountHook)
87+
removeCleanup(unmountHook)
8888
unmount()
8989
})
9090
}
9191

92-
addInternalCleanup(unmountHook)
92+
addCleanup(unmountHook)
9393

9494
return {
9595
result,
@@ -99,4 +99,4 @@ function renderHook(callback, { initialProps, wrapper } = {}) {
9999
}
100100
}
101101

102-
export { renderHook, cleanup, act,addCleanup }
102+
export { renderHook, cleanup, addCleanup,removeCleanup,act }

‎test/addCleanup.test.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

‎test/cleanup.test.js

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect } from 'react'
2-
import { renderHook, cleanup} from 'src'
2+
import { renderHook, cleanup,addCleanup,removeCleanup} from 'src/pure'
33

44
describe('cleanup tests', () => {
55
test('should flush effects on cleanup', async () => {
@@ -38,4 +38,98 @@ describe('cleanup tests', () => {
3838
expect(cleanupCalled[1]).toBe(true)
3939
expect(cleanupCalled[2]).toBe(true)
4040
})
41+
42+
test('should call cleanups in reverse order', async () => {
43+
let callSequence = []
44+
addCleanup(() => {
45+
callSequence.push('cleanup')
46+
})
47+
addCleanup(() => {
48+
callSequence.push('another cleanup')
49+
})
50+
const hookWithCleanup = () => {
51+
useEffect(() => {
52+
return () => {
53+
callSequence.push('unmount')
54+
}
55+
})
56+
}
57+
renderHook(() => hookWithCleanup())
58+
59+
await cleanup()
60+
61+
expect(callSequence).toEqual(['unmount', 'another cleanup', 'cleanup'])
62+
})
63+
64+
test('should wait for async cleanup', async () => {
65+
let callSequence = []
66+
addCleanup(() => {
67+
callSequence.push('cleanup')
68+
})
69+
addCleanup(async () => {
70+
await new Promise((resolve) => setTimeout(resolve, 10))
71+
callSequence.push('another cleanup')
72+
})
73+
const hookWithCleanup = () => {
74+
useEffect(() => {
75+
return () => {
76+
callSequence.push('unmount')
77+
}
78+
})
79+
}
80+
renderHook(() => hookWithCleanup())
81+
82+
await cleanup()
83+
84+
expect(callSequence).toEqual(['unmount', 'another cleanup', 'cleanup'])
85+
})
86+
87+
test('should remove cleanup using removeCleanup', async () => {
88+
let callSequence = []
89+
addCleanup(() => {
90+
callSequence.push('cleanup')
91+
})
92+
const anotherCleanup = () => {
93+
callSequence.push('another cleanup')
94+
}
95+
addCleanup(anotherCleanup)
96+
const hookWithCleanup = () => {
97+
useEffect(() => {
98+
return () => {
99+
callSequence.push('unmount')
100+
}
101+
})
102+
}
103+
renderHook(() => hookWithCleanup())
104+
105+
removeCleanup(anotherCleanup)
106+
107+
await cleanup()
108+
109+
expect(callSequence).toEqual(['unmount', 'cleanup'])
110+
})
111+
112+
test('should remove cleanup using returned handler', async () => {
113+
let callSequence = []
114+
addCleanup(() => {
115+
callSequence.push('cleanup')
116+
})
117+
const remove = addCleanup(() => {
118+
callSequence.push('another cleanup')
119+
})
120+
const hookWithCleanup = () => {
121+
useEffect(() => {
122+
return () => {
123+
callSequence.push('unmount')
124+
}
125+
})
126+
}
127+
renderHook(() => hookWithCleanup())
128+
129+
remove()
130+
131+
await cleanup()
132+
133+
expect(callSequence).toEqual(['unmount', 'cleanup'])
134+
})
41135
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /