material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

Textfield's autofocus in modal does not works with StrictMode and ReactDOM.createRoot

Open jderusse opened this issue 2 years ago • 15 comments

Duplicates

  • [X] I have searched the existing issues

Latest version

  • [X] I have tested the latest version

Current behavior 😯

When switching to ReactDOM.createRoot, opening a Dialog containing a TextField with autofocus, the field is not focused.

Expected behavior 🤔

No response

Steps to reproduce 🕹

reproducer: reproducer : https://codesandbox.io/s/dreamy-kowalevski-8eu5fb?file=/src/index.js

Context 🔦

The Dialog + field

export default function App() {
  const [openPopup, setOpenPopup] = useState(false);
  return (
    <>
      <Button onClick={() => setOpenPopup(true)}>
        Open Modal
      </Button>
      <Dialog open={openPopup}>
        <DialogContent>
          <TextField autoFocus />
        </DialogContent>
      </Dialog>
    </>
  );
}

When initializing the project with the previous version of react it worked. But react triggered the deprecation about switching to ReactDOM.createRoot

import { render } from "react-dom";
render(
  <StrictMode>
    <App />
  </StrictMode>,
  document.getElementById("root")
);

When switching to ReactDOM.createRoot it does not worked

ReactDOM.createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);

But, if I remove the StrictMode, it work

ReactDOM.createRoot(document.getElementById("root")).render(
  <App />
);

Your environment 🌎

`npx @mui/envinfo`
  System:
    OS: Linux 5.10 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
  Binaries:
    Node: 17.9.0 - /usr/bin/node
    Yarn: 3.1.1 - /usr/local/bin/yarn
    npm: 8.10.0 - /usr/bin/npm
  Browsers:
    Chrome: 102.0.5005.61
    Firefox: 91.10.0esr
  npmPackages:
    @emotion/react: ^11.9.0 => 11.9.0 
    @emotion/styled: ^11.8.1 => 11.8.1 
    @mui/base:  5.0.0-alpha.83 
    @mui/icons-material: ^5.8.2 => 5.8.2 
    @mui/material: ^5.8.2 => 5.8.2 
    @mui/private-theming:  5.8.0 
    @mui/styled-engine:  5.8.0 
    @mui/system:  5.8.2 
    @mui/types:  7.1.3 
    @mui/utils:  5.8.0 
    @types/react: ^18.0.10 => 18.0.10 
    react: ^18.1.0 => 18.1.0 
    react-dom: ^18.1.0 => 18.1.0 
    typescript: ^4.7.2 => 4.7.2 

jderusse avatar Jun 03 '22 10:06 jderusse

Thanks for the bug report - it's an interesting one! Would you like to dive deeper and investigate the issue?

michaldudak avatar Jun 03 '22 11:06 michaldudak

Check this https://reactjs.org/docs/strict-mode.html#warning-about-legacy-string-ref-api-usage

smitgol avatar Jun 05 '22 11:06 smitgol

@smitgol how does it relate to the problem? There are no warnings about refs.

michaldudak avatar Jun 07 '22 11:06 michaldudak

Tried to dig into the issue, but internal's react is too complicated for me :(

jderusse avatar Jun 07 '22 11:06 jderusse

Seems like using a timeout is a workaround. Updated sandbox, https://codesandbox.io/s/nostalgic-fermat-4eyjxr?file=/src/App.jsx

nrayburn-tech avatar Jul 18 '22 05:07 nrayburn-tech

I ran into this same issue today. Maybe it's related to https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state

React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.

You can swap the <Textfield /> for a normal <input /> and get the same result.

Here is the same sandbox without StrictMode, but with a double setTimeout that trigger the modal to open/hide/open quickly. It also breaks the autofocus. https://codesandbox.io/s/wandering-dust-vptu4u?file=/src/index.js

kroyee avatar Aug 12 '22 18:08 kroyee

Can confirm that disabling <StrictMode> resolves the issue. Likely the mount/remount behaviour causing the issue.

joel1st avatar Aug 18 '22 22:08 joel1st

The workaround does not work for me. Is it even possible to programmatically focus the TextField component?

radoslavkarlik avatar Oct 11 '22 12:10 radoslavkarlik

Any updates on this? I can confirm this is still an issue as of @mui/material 5.11.10.

sehcheese avatar Feb 27 '23 21:02 sehcheese

Me as well, observing the same behavior.

DCMattyG avatar Mar 02 '23 00:03 DCMattyG

This came up in a StackOverflow issue: https://stackoverflow.com/questions/75644447/autofocus-not-working-on-open-form-dialog-with-button-component-in-material-ui-v. I believe it is a "bug" in the restore-focus functionality in FocusTrap -- if I add the disableRestoreFocus prop to the Dialog the auto focus works. When the incorrect behavior occurs, focus does go briefly to the text field but then returns immediately to the button.

I believe the restore focus functionality is executing on open of the dialog. In strict mode with the dev react build, the FocusTrap component is being mounted twice, and the unmount after the first mount causes focus to be restored to the button.

I'm not seeing any easy way to fix this. It is correct for focus to be restored to the button during unmount, but I don't think the dialog and text field DOM elements are actually being unmounted -- it's just a re-execution of their mount lifecycle. So the second mount brings focus back to the dialog, but the text field auto-focus functionality doesn't re-execute since it was never removed/re-added to the DOM. This doesn't seem to be as much a bug in FocusTrap as it is an unfortunate side effect of the React 18 dev build strict mode behavior.

ryancogswell avatar Mar 06 '23 00:03 ryancogswell

Just encountered this as well. Looks like there's two options judging from the thread history for those on React 18+. Thanks for everyone who contributed.

  1. Ensure that the TextField autofocuses in the intended way in the production build and leave everything else as is.
  2. Set disableRestoreFocus prop to true on the Dialog component but lose that functionality, which is described as follows:

If true, the focus trap will not restore focus to previously focused element once focus trap is hidden or unmounted.

So, if the user's focus is inside a TextField, and they click a button or perform some action that causes a Dialog to open, the focus will not automatically return to that TextField when the Dialog is closed. Instead, they'll need to manually click into it again.

SikandAlex avatar Mar 17 '23 07:03 SikandAlex

Adding an update for Dec 2023

Can still reproduce this TextField inside Dialog autoFocus behaviour on

"@mui/material": "^5.15.0" "react": "^18.2.0"

@SikandAlex's solution works for me disableRestoreFocus on the Dialog with autoFocus on the child TextField

Strict Mode is enabled

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

I'm also using Vite tooling, but no special configurations here.

PathToLife avatar Dec 14 '23 03:12 PathToLife

Добавление обновлений за декабрь 2023 г.

Все еще можно воспроизвести это TextField внутри поведения автофокусировки диалога на

"@mui/material": "^5.15.0" "react": "^18.2.0"

@SikandAlexрешение работает для меня disableRestoreFocusв ближайшем окне с autoFocusдополнительным текстовым полем

Строгий режим включен

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Я также использую инструменты Vite, но никаких специальных настроек здесь нет.

https://github.com/mui/material-ui/assets/95183545/0a1ca141-efda-4241-b918-c8c135fca649

When using disableRestoreFocus the focus on the button is broken when closing the modal window via the keyboard

ArturKustyaev avatar Mar 20 '24 10:03 ArturKustyaev

I also encountered this problem. Thanks for all the answers above! Here's a quick recap of what worked for me.

It should work if your TextField is in a Dialog, and you're using React 18+ with Strict Mode enabled and MUI 15+

  1. The Dialog should have the disableRestoreFocus prop.
  2. The TextField in the Dialog should have the autoFocus prop.
<Dialog disableRestoreFocus {...otherProps}>
  ...
  <TextField autoFocus {...otherProps}/>
</Dialog>

In certain cases this has the downside mentioned in @SikandAlex's comment above but thankfully that did not apply to me 😅

So, if the user's focus is inside a TextField, and they click a button or perform some action that causes a Dialog to open, the focus will not automatically return to that TextField when the Dialog is closed. Instead, they'll need to manually click into it again.

drecali avatar Apr 24 '24 01:04 drecali