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

different behavior between React and Preact w/ React-MUI #4710

Open
1 task
krskrs opened this issue Feb 25, 2025 · 2 comments
Open
1 task

different behavior between React and Preact w/ React-MUI #4710

krskrs opened this issue Feb 25, 2025 · 2 comments
Labels

Comments

@krskrs
Copy link

krskrs commented Feb 25, 2025

  • Check if updating to the latest Preact version resolves the issue
    No

Describe the bug
I stumbled upon a different behavior between React and Preact w/ React-MUI

To Reproduce

I've found out that the React-MUI SpeedDial component works differently on React and Preact, making its usage on Preact very problematic.

First thing, the difference seems to exist on mobile only (touch behavior?) (must be tested with the desktop browser in mobile emulation)

It looks like Preact is handling/sending some events differently because, after opening the speeddial and clicking on the RED button, on Preact, an onMouseLeave event seems to be fired on the SpeedDial component, making it invoke its internal onClose callback (please see the console and the code). On react, on the contrary, the click on the button is correctly caught by the button, which will fire its onClick handler and the onMouseLeave event won't be fired.

SpeedDial Component
https://mui.com/material-ui/react-speed-dial/

SpeedDial Component Internal related references:

https://github.com/mui/material-ui/blob/6f2b908baa7863e2cbf0ec4518dc90ccfd53597b/packages/mui-material/src/SpeedDial/SpeedDial.js#L277
https://github.com/mui/material-ui/blob/6f2b908baa7863e2cbf0ec4518dc90ccfd53597b/packages/mui-material/src/SpeedDial/SpeedDial.js#L410
https://github.com/mui/material-ui/blob/6f2b908baa7863e2cbf0ec4518dc90ccfd53597b/packages/mui-material/src/SpeedDial/SpeedDial.js#L413

React reproduction:
https://codesandbox.io/p/devbox/wonderful-kate-xjt857?workspaceId=ws_LCddBC1t6QqksLkm3bx3c

Preact reproduction:
https://codesandbox.io/p/sandbox/66xhq9

As a side note, I also tested this with preact 10.0.0, where the debounce logic had changed. The behavior slightly changed, although it still wasn't identical to react. However, after I investigated further, this doesn't seem to be much relevant to the actual issue itself.

Expected behavior
What should have happened when following the steps above?

work like React, the Speeddial shouldn't close when clicking the red button, and the onMouseLeave event shouldn't be emitted(?)

@krskrs
Copy link
Author

krskrs commented Feb 25, 2025

UPDATE: on further debugging, it looks like there is more. There seems to be an event: 'focusout' which is fired in Preact, and (I suppose) not in React.. uhm.. This event is captured here:

https://github.com/mui/material-ui/blob/6f2b908baa7863e2cbf0ec4518dc90ccfd53597b/packages/mui-material/src/SpeedDial/SpeedDial.js#L277

and causes the unwanted behavior.

So it looks like preact is firing a focusout event through these event listeners (
onBlur={handleClose}
onMouseLeave={handleClose}
)?

Perhaps the apparent event mismatch can be due to this?

} else if (lowerCased === 'onblur') {

And then, the mui code has some conditions on the 'blur' event, but since here it gets a 'focusout', the behavior differs.

https://github.com/mui/material-ui/blob/6f2b908baa7863e2cbf0ec4518dc90ccfd53597b/packages/mui-material/src/SpeedDial/SpeedDial.js#L287

@rschristian
Copy link
Member

rschristian commented Feb 26, 2025

First off, mandatory disclaimer that Preact uses native web events whilst React uses a custom event system that diverges to varying degrees.

The native blur event does not bubble which is the defining characteristic that sets it apart from focusout. React, however, sets their blur event to bubble through their custom event system. To better match this, we alias blur events to focusout in preact/compat, which you've spotted.

The issue here however is that MUI doesn't correctly capture native focusout events: when you click on the SpeedDial button, it becomes focused in the browser. Clicking anything else will trigger the focusout event so the event handler needs to account for this with something like if (event.currentTarget.contains(event.relatedTarget)) return, essentially bailing out of the handler if the relatedTarget (the SpeedDial pop-up) is a child of currentTarget (the SpeedDial button). They don't do this, however, so the popup closes.

And then, the mui code has some conditions on the 'blur' event, but since here it gets a 'focusout', the behavior differs.

Just to clarify, if we didn't alias the event to focusout, the event handler in MUI would never run as that's not how that event works in the browser.


I'm not sure there's much we can do here, this is a rather big divergence in events/event handling which is generally out of scope for Preact. Using patch-package might be a good solution, only a single line needs to be inserted into MUI's handleClose function.

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

No branches or pull requests

2 participants