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

[SwipeableDrawer] Fix React 18 issues #34505

Merged
merged 7 commits into from Oct 26, 2022

Conversation

mnajdova
Copy link
Member

@mnajdova mnajdova commented Sep 28, 2022

Fixes #30414

Steps to reproduce:

  1. Open the sandbox
  2. Open the rendered demo in new tab, inspect element and change the device to mobile
  3. Try swapping the drawer and you will notice the two problems mentioned in the issue:
    • Discovery opens drawer completely
    • Partially opened drawer (> hysteresis) is not opened completely (it gets stuck at the position it was when the touch move ended)

❌ Current master Sandbox

✅ This PR Sandbox

Remarks

Not happy about the keepMounted, but if that is not set, the paperRef.current is undefined when the touch start event on the body is invoked. At first I thought using flushAsync on the set state before the check would be sufficient, as the DOM should be updated after the callback is being invoked, at least that is how I understand based on the description https://reactjs.org/docs/react-dom.html#flushsync, but that's not the case. Interestingly, using only keepMounted without the flushSync doesn't work too.

@@ -234,7 +235,9 @@ const SwipeableDrawer = React.forwardRef(function SwipeableDrawer(inProps, ref)
}
claimedSwipeInstance = null;
touchDetected.current = false;
setMaybeSwiping(false);
flushSync(() => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes the part of the issue where the partially opened drawer (> hysteresis) is not opening the drawer completely. Could probably go as a standalone fix.

flushSync(() => {
setMaybeSwiping(true);
});

if (!open && paperRef.current) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed that here paperRef.current will be defined for sure because of the flushSync before, which would change the setMaybeSwiping state, which is used for setting the open prop on the Drawer, and hence the modal. However, this is not the case, so I needed to add the keepMounted prop on the ModalProps, to be sure that the ref would be assigned to a node.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use ModalProps={{ keepMounted: true }} anyways at the moment

@mnajdova mnajdova added the component: SwipeableDrawer The React component. label Sep 28, 2022
@mui-bot
Copy link

mui-bot commented Sep 28, 2022

Details of bundle changes

Generated by 🚫 dangerJS against 5b05a36

@mnajdova
Copy link
Member Author

mnajdova commented Sep 28, 2022

@eps1lon I requested review from you in case you have time to look in the changes in the PR, no hard feelings if you don't have time :) Maybe you will have a better understanding on why flushSync is not enough when invoked in the touch start event handler and we still need keepMounted so that the paperRef.current is defined on the next line. I am taking about these lines of code: https://github.com/mui/material-ui/pull/34505/files#diff-e0621affa43544c6351261b6f58061806969e4184f95f29e57c9e629fda9727aR494-R498

@mnajdova mnajdova added the PR: needs test The pull request can't be merged label Sep 28, 2022
@mnajdova mnajdova removed the PR: needs test The pull request can't be merged label Sep 29, 2022
@mnajdova mnajdova marked this pull request as ready for review September 29, 2022 10:51
@mnajdova mnajdova requested a review from a team September 29, 2022 12:11
@eps1lon
Copy link
Member

eps1lon commented Sep 29, 2022

All I remember is that the implementation of the drawer and swipeable-views was always a huge headache. Even before React 18. So this would require an extensive deep dive to understand why flushSync is required here that I don't have the time unfortunately.

Probably best to rewrite this component from scratch (ignoring unit tests and focusing on e2e behavior first) to ensure its compatibility with React 18.

@mnajdova
Copy link
Member Author

Probably best to rewrite this component from scratch (ignoring unit tests and focusing on e2e behavior first) to ensure its compatibility with React 18.

Alright, thanks for giving the insights. We will keep this in mind when working on the headless hook 👍

@mnajdova
Copy link
Member Author

cc @michaldudak

Signed-off-by: Marija Najdova <mnajdova@gmail.com>
@lukaselmer
Copy link
Contributor

Thank you @mnajdova for the fix.

We are experiencing the issue in our app, where we use ModalProps={{ keepMounted: true }} anyways. We would appreciate it if this PR would go forward - even without a rewrite as suggested by @eps1lon. It fixes a painful bug. The component without these fixes is unusable with React 18. And the issue is hard to spot after an upgrade to React 18 (so probably in production on many apps).

Copy link
Member

@michaldudak michaldudak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@mnajdova mnajdova merged commit d35d229 into mui:master Oct 26, 2022
@mnajdova
Copy link
Member Author

We are experiencing the issue in our app, where we use ModalProps={{ keepMounted: true }} anyways. We would appreciate it if this PR would go forward - even without a rewrite as suggested by @eps1lon. It fixes a painful bug. The component without these fixes is unusable with React 18. And the issue is hard to spot after an upgrade to React 18 (so probably in production on many apps).

@lukaselmer it will be available with the release next week.

@lukaselmer
Copy link
Contributor

Great, thank you @mnajdova @michaldudak!

@JoseBuendiaDigio
Copy link

JoseBuendiaDigio commented Nov 7, 2022

Hi, this is changing default behaviour of SwipeableDrawer component that by default until now (5.10.11) it had ModalProps={{ keepMounted: false }} and now (5.10.12) this is true.
Maybe should be advised as a breaking change or something like this. I think that at least should be changed in the docs.
Thanks in advance.

@michaldudak
Copy link
Member

@JoseBuendiaDigio thanks for pointing this out. We missed the docs. I've just created a PR to address this: #35076

daniel-rabe pushed a commit to daniel-rabe/material-ui that referenced this pull request Nov 29, 2022
feliperli pushed a commit to jesrodri/material-ui that referenced this pull request Dec 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: SwipeableDrawer The React component.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[SwipeableDrawer] Discovery and hysteresis broken in React v18
6 participants