Skip to content

Dialog fires onClose when open prop is set to false — controlled component contract violation #4888

@lasseklovstad

Description

@lasseklovstad

Version number

1.14.0

Description of the bug

When using Dialog as a controlled component (passing open as a boolean prop), setting open={false} from outside the dialog triggers onClose. The component internally calls dialog.close() to sync the native DOM element when the prop changes, which fires the native close event, which is then forwarded to onClose.

This violates the expected controlled component contract: onClose should only fire in response to a user gesture (backdrop click, close button, Escape key). It must not fire when the parent drives the close by changing the open prop. Analogously, a controlled <input> does not fire onChange when its value prop is updated externally.

Steps To Reproduce

  1. Mount a Dialog with a controlled open prop:

    const [open, setOpen] = useState(true);
    
    <Dialog
      open={open}
      onClose={() => console.log("onClose fired")}
    >
      <Button onClick={() => setOpen(false)}>Save</Button>
    </Dialog>
  2. Click Save — this sets open=false via React state, bypassing any dialog close mechanism entirely.

  3. Observe the console — "onClose fired" is logged even though the user did not interact with a close gesture on the dialog.

Additional Information

Expected Behavior

onClose fires only when the user triggers a close gesture on the dialog itself (backdrop click, close button, Escape key). Setting open={false} via props must be silent — it is the parent acknowledging the close, not initiating a new one.

Workaround

Deduplicate with a ref guard in the consuming code until this is fixed:

const firedRef = useRef(false);
if (open) firedRef.current = false;

const handleClose = () => {
  if (firedRef.current) return;
  firedRef.current = true;
  setOpen(false);
};

<Dialog open={open} onClose={handleClose}>
  ...
</Dialog>

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working

    Type

    No type

    Projects

    Status

    📥 Inbox

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions