diff --git a/.all-contributorsrc b/.all-contributorsrc index be8f304a..dc53c0ec 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -183,6 +183,17 @@ "contributions": [ "test" ] + }, + { + "login": "huchenme", + "name": "Hu Chen", + "avatar_url": "https://avatars3.githubusercontent.com/u/2078389?v=4", + "profile": "https://huchen.dev/", + "contributions": [ + "code", + "doc", + "example" + ] } ], "commitConvention": "none" diff --git a/README.md b/README.md index e40d2ad9..edef6733 100644 --- a/README.md +++ b/README.md @@ -164,11 +164,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Roman Gusev

📖
Adam Seckel

💻
keiya sasaki

⚠️ +
Hu Chen

💻 📖 💡 - + + This project follows the [all-contributors](https://allcontributors.org/) specification. diff --git a/docs/api-reference.md b/docs/api-reference.md index 53c7623a..16413d8f 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -10,6 +10,7 @@ route: '/reference/api' - [`renderHook`](/reference/api#renderhook) - [`act`](/reference/api#act) - [`cleanup`](/reference/api#cleanup) +- [`addCleanup`](/reference/api#addcleanup) --- @@ -147,6 +148,31 @@ variable to `true` before importing `@testing-library/react-hooks` will also dis --- +## `addCleanup` + +```js +function addCleanup( + callback: function(props?: any): any +): void +``` + +Callback to be called after `cleanup`. + +In some cases you might want to run some callback after internal `cleanup` happen, especially after +`unmount` happens in `cleanup`. If the sequence matters to you, you could use `addCleanup`. + +```js +import { addCleanup } from '@testing-library/react-hooks' + +jest.useFakeTimers() + +addCleanup(() => { + jest.runOnlyPendingTimers() +}) +``` + +--- + ## Async Utilities ### `waitForNextUpdate` diff --git a/src/cleanup.js b/src/cleanup.js index c240b5e1..60fd5be8 100644 --- a/src/cleanup.js +++ b/src/cleanup.js @@ -1,19 +1,25 @@ import flushMicroTasks from './flush-microtasks' -let cleanupCallbacks = [] +let internalCleanupCbs = [] +let cleanupCbs = [] async function cleanup() { await flushMicroTasks() - cleanupCallbacks.forEach((cb) => cb()) - cleanupCallbacks = [] + internalCleanupCbs.forEach((cb) => cb()) + internalCleanupCbs = [] + cleanupCbs.forEach((cb) => cb()) +} + +function addInternalCleanup(callback) { + internalCleanupCbs.push(callback) } function addCleanup(callback) { - cleanupCallbacks.push(callback) + cleanupCbs.push(callback) } -function removeCleanup(callback) { - cleanupCallbacks = cleanupCallbacks.filter((cb) => cb !== callback) +function removeInternalCleanup(callback) { + internalCleanupCbs = internalCleanupCbs.filter((cb) => cb !== callback) } -export { cleanup, addCleanup, removeCleanup } +export { cleanup, addCleanup, addInternalCleanup, removeInternalCleanup } diff --git a/src/pure.js b/src/pure.js index 3b4a475d..e2427f64 100644 --- a/src/pure.js +++ b/src/pure.js @@ -1,7 +1,7 @@ import React, { Suspense } from 'react' import { act, create } from 'react-test-renderer' import asyncUtils from './asyncUtils' -import { cleanup, addCleanup, removeCleanup } from './cleanup' +import { cleanup, addCleanup, addInternalCleanup, removeInternalCleanup } from './cleanup' function TestHook({ callback, hookProps, onError, children }) { try { @@ -84,12 +84,12 @@ function renderHook(callback, { initialProps, wrapper } = {}) { function unmountHook() { act(() => { - removeCleanup(unmountHook) + removeInternalCleanup(unmountHook) unmount() }) } - addCleanup(unmountHook) + addInternalCleanup(unmountHook) return { result, @@ -99,4 +99,4 @@ function renderHook(callback, { initialProps, wrapper } = {}) { } } -export { renderHook, cleanup, act } +export { renderHook, cleanup, act, addCleanup } diff --git a/test/addCleanup.test.js b/test/addCleanup.test.js new file mode 100644 index 00000000..8e74e73a --- /dev/null +++ b/test/addCleanup.test.js @@ -0,0 +1,27 @@ +import { useEffect } from 'react' +import { renderHook, addCleanup } from 'src' + +let callSequence = [] +addCleanup(() => { + callSequence.push('cleanup') +}) +addCleanup(() => { + callSequence.push('another cleanup') +}) + +describe('addCleanup tests', () => { + test('first', () => { + const hookWithCleanup = () => { + useEffect(() => { + return () => { + callSequence.push('unmount') + } + }) + } + renderHook(() => hookWithCleanup()) + }) + + test('second', () => { + expect(callSequence).toEqual(['unmount', 'cleanup', 'another cleanup']) + }) +})