react-modal icon indicating copy to clipboard operation
react-modal copied to clipboard

Bug: Modal with `closeTimeoutMS` unrecoverable with Suspense

Open h3rmanj opened this issue 2 years ago • 8 comments

Summary:

Passing closeTimeoutMS to an open Modal makes it unrecoverable if it's part of a render that suspends.

Steps to reproduce:

  1. Create a component that contains an open modal, and something that can suspend
  2. Suspend it after initial render
  3. Modal can't recover

Expected behavior:

Modal should be open.

Link to example of issue:

https://codesandbox.io/s/closetimeoutms-and-suspense-nfmelh?file=/src/App.tsx

Additional notes:

If the closeTimeoutMS prop is removed from the codesandbox link, everything works as expected.

h3rmanj avatar Nov 14 '22 12:11 h3rmanj

@h3rmanj This is super weird...why is it triggering the fallback if there is still content been displayed? (the h1 node)

diasbruno avatar Nov 14 '22 15:11 diasbruno

It is the lazy component that triggers the suspense. Probably, suspense uses conditional rendering on their side and react-modal doesn't play well with it.

diasbruno avatar Nov 14 '22 15:11 diasbruno

React-Modal: Cannot register modal instance that's already open in Unknown

:thinking: it must be failing to remove the instance from the manager.

diasbruno avatar Nov 14 '22 15:11 diasbruno

Do note you have to reload the sandbox each time you try again, as it resolves and triggers the fallback only once per load.

Which h1 node are you referring? The developer I am opted to use it for all displayed elements 😅

In the docs it is noted that suspense will call layout effect cleanups, which might include componentDidUnmount etc.

Do note that this is a low-priority bug, which can be easily solved by localizing suspenses etc. But it was something we met, and used some time to understand why happened. I would think that the component would respect the isOpen prop no matter what.

h3rmanj avatar Nov 14 '22 15:11 h3rmanj

My bad...I thought that the h1 tag in the sandbox would not allow to trigger the suspense, but actually it comes from the LazyComponent.

react-modal has a small manager to deal with nested modals and dangling references to modals.

In this case, since it doesn't trigger the logic to remove the instance from the manager, it will fail when it tries to open a new instance.

You must be getting this log message:

React-Modal: Cannot register modal instance that's already open in Unknown

diasbruno avatar Nov 14 '22 15:11 diasbruno

Right, so if I remove closeTimeoutMS (codesandbox), what appears to be expected behavior is actually showing an old instance that should've been removed?

h3rmanj avatar Nov 14 '22 15:11 h3rmanj

If suspense is been triggered, than it must create a new instance of the Modal, because that one was already "detroyed".

https://github.com/reactjs/react-modal/blob/8a7426898cc53844dd2d3043e16d429a1a4942ad/src/helpers/portalOpenInstances.js#L9-L18

Unless react is doing some black magic to keep the previously rendered tree, and only append what is in "real suspense".

diasbruno avatar Nov 14 '22 15:11 diasbruno

If I understand the docs right, if the component has initially mounted, but then shows the fallback later, react will preserve the state of the components until the suspense promise is resolved.

h3rmanj avatar Nov 14 '22 16:11 h3rmanj