-
Notifications
You must be signed in to change notification settings - Fork 231
Feature: add addCleanup util function #432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## master #432 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 4 4
Lines 107 109 +2
Branches 19 19
=========================================
+ Hits 107 109 +2
Continue to review full report at Codecov.
|
The reason I need to run something after unmount is that in my hook the cleanup create a timer to cleanup things of an instance of a Class which is deep in the dependency tree. hook.js import service from './service'
function myHook() {
useEffect(() => {
return () => {
service.cleanup(id)
}
})
} service.js import TheService from './theservice'
//...
export default new TheService() theservice.js export default class TheService {
constructor() {
this.someList = new Set()
}
cleanup(id) {
setTimeout(() => {
this.someList.remove(id)
}, 2000)
}
} so we will need to make sure |
@mpeyper could you help review this PR? thanks a lot |
I understand the issue you're trying to solve here, but I feel like the use-case is quite niche and I believe it might be possible to achieve by opting out of auto cleanup and handling it yourself for this scenario: import { renderHook, cleanup } from '@testing-library/react-hooks/pure'
afterEach(async () => {
await cleanup()
console.log('afterUnmount')
})
test('testing', () => {
const hookWithCleanup = () => {
useEffect(() => {
return () => {
console.log('cleanup')
}
})
}
renderHook(() => hookWithCleanup())
}) I also feel like there are a few things in the implementation here that go a bit against my vision for this library. Firstly, there is an implicit, but no obvious connection between the afterUnmount(() => {
console.log('this got called')
})
test('example', () => {
const { result, unmount } = renderHook(() => hookWithCleanup())
// a bunch of test...
unmount() // afterUnmount is called now
// some more test...
expect(result.current).toBe('something')
}) This is an example of something that makes perfect sense when you are writing the test, but bring someone else in or come back to this in a year's time and there are no threads to follow between when test('example', () => {
const { result, unmount, afterUnmount } = renderHook(() => hookWithCleanup())
afterUnmount(() => {
console.log('this got called')
})
// a bunch of test...
unmount() // afterUnmount is called now
// some more test...
expect(result.current).toBe('something')
}) (yes, you can just move the call into the test, but having it as a root export makes the alternative possible) Secondly, calling the function twice undoes the original: afterUnmount(() => {
console.log('this wont get called')
})
afterUnmount(() => {
console.log('this got called')
})
test('example', () => {
renderHook(() => hookWithCleanup())
}) This is unlikely to occur in a small test file, but with nested Speaking of cleanups, would exposing test('example', () => {
const { addCleanup } = renderHook(() => hookWithCleanup())
addCleanup(() => {
console.log('this got called')
})
}) or even import { renderHook, addCleanup } from '@testing-library/react-hooks'
addCleanup(() => {
console.log('this got called')
})
test('example', () => {
renderHook(() => hookWithCleanup())
}) (It's more acceptable to me here in the global context because cleanup is implicitly global and not actually tied to This has the caveat that it would not play after a manual unmount, but it is a more generic solution to the problem of running additional cleanup after the built in cleanup has occurred. We'd probably want to support To summarise, I don't think this utility is useful enough to enough people for me to take on the maintenance of it in this library. I might be wrong and I'm happy to hear alternative points of view. |
Hi @mpeyper, thanks for the detailed write-up and propose some other solutions.
it will work, but it also means I will need to call manual
for the case you provided, it could simply write unmount()
console.log('after unmount') so the
This is a really issue which I did not notice, which might require some additional work, if you think the overall proposal is good
if it is within a single test, people could just call cleanup manually before the end of the test, this API is not solving this issue
Currently addCleanup(() => {
console.log('this got called')
}) is added globally first, the cleanup will be called before unmount happen, but what I want to achieve is to call something after unmount, secondly, the cleanup added will be removed in each tests (https://github.com/testing-library/react-hooks-testing-library/blob/master/src/cleanup.js#L8), so In summary, I want to continue using auto cleanup to unmount after each test, as there is no other way to unmount after tests without repeating (unless we have some APIs like below), but still run some callback after unmounting. afterEach(() => {
unmountTheHook()
afterUnmount()
}) If you have better ideas, please let me know |
I don't have a huge amount of time for a reply, so just a quick note for now
|
yes, I understand, but how to make sure some additional things run after the |
I'm sorry @huchenme I don't quite follow. If you opt out of auto cleanup then you control when the cleanup happens and what code runs before or after it. You won't need to call import { renderHook, cleanup } from '@testing-library/react-hooks/pure'
afterEach(async () => {
await cleanup()
console.log('afterUnmount')
})
test('testing', () => {
const hookWithCleanup = () => {
useEffect(() => {
return () => {
console.log('cleanup')
}
})
}
renderHook(() => hookWithCleanup())
})
hmm, yes. I see what you mean. Purely exposing Just spitballing the idea a little bit, what if the global This might be a bit of a bigger build than just what you require, so if you want to make an issue and reference this PR in it, I'm happy to explore it myself. By all means though, if you want to have a go and modify this PR along those lines, I'll gladly review it. My issue really is with this your proposal as written is that |
64155ce
to
01918d9
Compare
Sorry I have not thought about this could achieve what I wanted import { renderHook, cleanup } from '@testing-library/react-hooks/pure'
afterEach(async () => {
await cleanup()
console.log('afterUnmount')
}) But after reading your suggestions on |
Sorry @huchenme, I am planning on coming back to this, I've just been busy the last few days and might not yet to it for another few. |
Hi @huchenme, I finally found enough time to give this a proper look. While I was reviewing, I struggled with the idea that the cleanups were going to be global and would persist between tests, if they were only registered in a specific After playing around with that idea for a while, it started to feel almost more correct that the last cleanup added should be the first one to run. This is similar to how things like The changes I pushed to your branch would make your example look something likes this: import { renderHook, cleanup, addCleanup } from '@testing-library/react-hooks'
beforeEach(() => {
addCleanup(() => console.log('afterUnmount'))
})
test('testing', () => {
const hookWithCleanup = () => {
useEffect(() => {
return () => {
console.log('cleanup')
}
})
}
renderHook(() => hookWithCleanup())
}) How does that look to you? Workable? I also added a few niceties like a I'm still on the fence to how much this functionality will be used, but let me know if you are ok with my changes as I feel like there is very little overhead in maintaining this version (it's basically the same functionality we used internally), so if you can find some value in it, other might too. |
Hi @mpeyper thanks for the changes, I think it works for my case! |
I've thought long and hard about this, and I've decided to merge it. I've got a few other changes I want to make so it might not make a release immediately, but it'll come along in the next version. |
🎉 This PR is included in version 3.5.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
What:
added
addCleanup
util function to be called afterunmount
happen in cleanup processWhy:
to add additional cleanups besides internal cleanups (unmount) and these callbacks could be called automatically in auto cleanup process
it will log
cleanup
and thenaddCleanup
How:
added
addCleanup
util function to add additional cleanups and to be called after internalcleanup
happenChecklist: