Skip to content
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

fix(cleanup): Cleanup should flush microtask queue after unmounting containers #632

Merged
merged 2 commits into from Jun 25, 2020

Conversation

kamranayub
Copy link
Contributor

@kamranayub kamranayub commented Apr 7, 2020

What:

The cleanup routine should unmount mounted containers first to trigger all the effect cleanup routines and then flush the microtask queue.

Why:

When using some libraries such as react-query or react-modal, there can be unmounting routines that schedule immediate microtasks to perform cleanup.

I've documented a specific case here in react-query where I was seeing testing inconsistencies unless I explicitly called unmount during the test.

In React Modal specifically, here's a snippet from one of their componentWillUnmount cleanup routines:

https://github.com/reactjs/react-modal/blob/v3.11.2/src/components/Modal.js#L153-L172

How:

I moved the await flush() to be after unmounting containers. I've added a test case that fails when using setImmediate during an effect cleanup routine.

Checklist:

  • Documentation added to the
    docs site N/A
  • Tests
  • Typescript definitions updated N/A
  • Ready to be merged

I think this is a safe (and correct) change but it is a change in behavior that could have side effects.

@codesandbox-ci
Copy link

codesandbox-ci bot commented Apr 7, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 5048091:

Sandbox Source
beautiful-spence-vrsyc Configuration
inspiring-golick-fj7ym Configuration

@kamranayub kamranayub marked this pull request as ready for review April 7, 2020 20:33
@kamranayub
Copy link
Contributor Author

Hmm, CI seems to be stalled/not reporting? Should I push a non-change again? 🤔

@kentcdodds
Copy link
Member

Thanks for this @kamranayub!

I'm conflicted about this one. I think it's good, but I'm worried that there could be unintended consequences of people missing things their components are doing that should be handled in their tests... Hmmm...

I think I'm good to merge this, but I'd love someone else to take a look at this as well.

@kamranayub
Copy link
Contributor Author

I agree, my hope today was to make a CodeSandbox that showcased this more clearly with either react-query and react-modal, to see if the root cause could be fixed in underlying libraries. Part of me also thinks it could be a real use case, as you may expect microtask queue to work like this in a real usage context even on unmount. But I don't know enough to say with certainty.

@codecov
Copy link

codecov bot commented Apr 8, 2020

Codecov Report

Merging #632 into master will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##            master      #632   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         4    +1     
  Lines          103       141   +38     
  Branches        15        23    +8     
=========================================
+ Hits           103       141   +38     
Impacted Files Coverage Δ
src/pure.js 100.00% <100.00%> (ø)
src/index.js 100.00% <0.00%> (ø)
src/fire-event.js 100.00% <0.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 91f0c75...5048091. Read the comment docs.

@kamranayub
Copy link
Contributor Author

I have narrowed it down with a reproduction repo and isolated case of using react-query and react-modal, I will be trying to see if it's one or the other or both now. I will be trying to narrow it down even further I hope!

@kamranayub
Copy link
Contributor Author

@kentcdodds Good news, I seem to have been able to reproduce my issue solely with react-query by itself under certain conditions. I think I can tackle the issue over there first and see if we can fix the underlying cause of this.

@kamranayub
Copy link
Contributor Author

I was able to fix the underlying issue in react-query so I don't think is needed (and to your point, I wouldn't have discovered this issue if this was changed).

Closing for now! 👍

@kentcdodds
Copy link
Member

Ok, so here's some fun news. I believe we actually do need this change. Check this out:

const React = require('react')
const { render } = require('@testing-library/react')

function Comp() {
  const [state, setState] = React.useState()
  React.useEffect(() => {
    setTimeout(() => setState('blah'))
  }, [state])
  return null
}

test('runs', () => {
  render(React.createElement(Comp))
})

You'll notice if you run this, it passes without issue or warning. But it shouldn't! What should be happening here is

@kentcdodds kentcdodds reopened this May 26, 2020
@kentcdodds
Copy link
Member

Whoops, I think I didn't finish that thought and accidentally commented 😅

Honestly, I'm not sure what to do here and I'd love feedback. Please see: https://youtu.be/h7B3Ouv0gSE

@kentcdodds
Copy link
Member

Actually, I'm pretty sure we should leave things as-is. Flushing first will mean that useEffect callbacks will get called if needed which will queue up cleanup callbacks which will then get called by the unmounting process. If we swap this, we could miss calling cleanups (or calling stale cleanups).

@kentcdodds
Copy link
Member

I think we've figured this all out now. I haven't seen an act warning in a while :)

@kentcdodds kentcdodds closed this Jun 24, 2020
@kentcdodds
Copy link
Member

jk, this literally popped up again and I'm sick of it. We're merging this thing.

@kentcdodds kentcdodds reopened this Jun 25, 2020
@kentcdodds kentcdodds merged commit aac2e44 into testing-library:master Jun 25, 2020
@kentcdodds
Copy link
Member

🎉 This PR is included in version 10.4.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@kentcdodds
Copy link
Member

FWIW, this fixed my issues and I'm pretty sure it's not causing more harm than good so that's good news :)

@kentcdodds
Copy link
Member

@all-contributors please add @kamranayub for code and tests

@allcontributors
Copy link
Contributor

@kentcdodds

I've put up a pull request to add @kamranayub! 🎉

@stefcameron
Copy link

In case anyone stumbles on this like I did where after this patch update, ALL your tests are broken -- except for the first one -- that runs in the suite, and the error is related to using the https://github.com/davidtheclark/focus-trap-react package with its initialFocus option to set focus to an initial element via a query selector (the error being the element doesn't exist), update to focus-trap-react@7.0.1 that was just published yesterday. That fixed the timing issues for me, thanks to the package now depending on a more recent version of the underlying focus-trap package.

focus-trap delays setting focus to the initial element for a single frame/event loop pass and this seems related to what broke with focus-trap v4.x (from focus-trap-react v6.0.0). Curiously, though, I didn't have to use await waitFor(() => expect(...)). Something changed in focus-trap v5.1.0 (from focus-trap-react v.7.0.1) that somehow resolves the timing issue; can't tell what, exactly, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants