Repository: mui/material-ui (TypeScript) — Bug Fix

Issue:
Dialog backdrop is persisted when initialized as open but then immediately closed
This should not be a duplicate of the transition issue as I'm not using a Transition, this is a default Dialog component (reproduced below using the Component API Demo page).

Essentially I think flipping the `open` prop too quickly results in the backdrop being persisted. I **know** ideally I should get it to initialize with the right value, but the prop is driven off of Redux and initially I won't have the updated state until some other components that handle stuff are run (this is auth related, so validating tokens and stuff all happens outside this component).

<!--
    Thank you very much for contributing to Material-UI by creating an issue! ❤️
    To avoid duplicate issues we ask you to check off the following list.
-->

<!-- Checked checkbox should look like this: [x] -->
- [x] This is a v1.x issue. <!-- (v0.x is no longer maintained) -->
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior

When dialog is mounted initially with open `true` and then immediately reset back to `false`, backdrop should not be shown or block UI.

## Current Behavior

When Dialog is initially mounted as `open={true}` but then immediately set to `false` (in my case, due to `getDerivedStateFromProps` that flips it immediately due to business logic), the UI backdrop is still being persisted and blocks the UI.

## Steps to Reproduce

I changed the demo file to start with `open` set to `true` and then on mount, set it to `false` to emulate the situation I'm in.

Link: https://codesandbox.io/s/14w19w1px4

1. Start app
2. Try to click somewhere like the button

## Context
<!---
    What are you trying to accomplish? How has this issue affected you?
    Providing context helps us come up with a solution that is most useful in the real world.
-->

## Your Environment
<!---
    Include as many relevant details about the environment with which you experienced the bug.
    If you encounter issues with typescript please include version and tsconfig.
-->

| Tech         | Version |
|--------------|---------|
| Material-UI  | v3.0.2  |
| React        | 16.3.2        |
| Browser      | Chrome 68        |
| TypeScript   | Yes (no in demo)        |
| etc.         |         |



Discussion:
After you set open to false it does not set exited property of modal to true. So hidden class does not applied and dialog covers all area. Am I right? @kamranayub 

I think that `handleExit` could not be called by a reason that i don't know after `getDerivedStateFromProps`.
I could be related with `getHasTransition` method.

Now i have a bigger problem: My daughter wants play with me:). So maybe later i will debug it. 

@oliviertassinari: Please verify us , are we in right way?

As far as I could look into the issue, it's not related to the backdrop. You can remove it and still experience the problem. The `onExited` hook of react-transition-group isn't called.
- Setting `disablePortal` on the Portal solves the problem
- Using a div over a Portal solves the problem
- Using NoSsr over the Portal maintains the issue.

So, we have it, it's a rendering order issue. We need the transition to render once with `open` true to get the `onExited` called when `open` is set to false.

I will leave you with that 😴 
@mbrn are you working on this?
@nathanmarks i will.
Thanks for your help!

On Mon, Sep 10, 2018, 22:54 Mehmet Baran <notifications@github.com> wrote:

> @nathanmarks <https://github.com/nathanmarks> i will.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/mui-org/material-ui/issues/12831#issuecomment-420138067>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAiaa_VdQUCG2r2rI72pRSFOHBhS03x2ks5uZzPhgaJpZM4WhzCx>
> .
>

@oliviertassinari You are right. When i change disablePortal props of Modal to true, the problem is being solved. Should i send disablePortal:true to Modal from Dialog or make Modal's default disablePortal value true?

What do you offer? @nathanmarks @oliviertassinari
> Should i send disablePortal:true to Modal from Dialog or make Modal's default disablePortal value true?

Please don't. It was just to identify what's going on. If few people are affected by this issue, @kamranayub and others can stick to the `disablePortal` workaround.
But if we can find a simple fix, not something that requires 50 LOCs, then yes, why not fixing it :). We could explore the `onEnter` hook or the`dialogRef` existence.
ok @oliviertassinari. @kamranayub you can set disablePortal={true} to solve your problem. 
I'll try it out today.

On Tue, Sep 11, 2018, 01:26 Mehmet Baran <notifications@github.com> wrote:

> ok @oliviertassinari <https://github.com/oliviertassinari>. @kamranayub
> <https://github.com/kamranayub> you can set disablePortal={true} to solve
> your problem.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/mui-org/material-ui/issues/12831#issuecomment-420161349>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAiaa3cEfgSzHmwM5wEXiYUJQ5aP0Re1ks5uZ1emgaJpZM4WhzCx>
> .
>

I actually managed to remove the need for a dialog in my use case so I'm no longer affected by this. I can close and if someone else finds a need for a fix, can re-open perhaps.
Re-opening this since it's a legitimate bug
Sure, no problem. `disablePortal={true}` does indeed work.

Resolving patch:
diff --git a/packages/material-ui/src/Modal/Modal.js b/packages/material-ui/src/Modal/Modal.js
--- a/packages/material-ui/src/Modal/Modal.js
+++ b/packages/material-ui/src/Modal/Modal.js
@@ -85,7 +85,7 @@ const Modal = React.forwardRef(function Modal(props, ref) {
   } = props;
 
   const theme = useTheme();
-  const [exited, setExited] = React.useState(!open);
+  const [exited, setExited] = React.useState(true);
   const modal = React.useRef({});
   const mountNodeRef = React.useRef(null);
   const modalRef = React.useRef(null);
diff --git a/packages/material-ui/src/Popper/Popper.js b/packages/material-ui/src/Popper/Popper.js
--- a/packages/material-ui/src/Popper/Popper.js
+++ b/packages/material-ui/src/Popper/Popper.js
@@ -68,7 +68,7 @@ const Popper = React.forwardRef(function Popper(props, ref) {
   }, [handlePopperRef]);
   React.useImperativeHandle(popperRefProp, () => popperRef.current, []);
 
-  const [exited, setExited] = React.useState(!open);
+  const [exited, setExited] = React.useState(true);
 
   const rtlPlacement = flipPlacement(initialPlacement);
   /**
diff --git a/packages/material-ui/src/Snackbar/Snackbar.js b/packages/material-ui/src/Snackbar/Snackbar.js
--- a/packages/material-ui/src/Snackbar/Snackbar.js
+++ b/packages/material-ui/src/Snackbar/Snackbar.js
@@ -126,7 +126,7 @@ const Snackbar = React.forwardRef(function Snackbar(props, ref) {
   } = props;
 
   const timerAutoHide = React.useRef();
-  const [exited, setExited] = React.useState(!open);
+  const [exited, setExited] = React.useState(true);
 
   // Timer that controls delay before snackbar auto hides
   const setAutoHideTimer = React.useCallback(


Test patch:
diff --git a/packages/material-ui/src/Modal/Modal.test.js b/packages/material-ui/src/Modal/Modal.test.js
--- a/packages/material-ui/src/Modal/Modal.test.js
+++ b/packages/material-ui/src/Modal/Modal.test.js
@@ -144,6 +144,27 @@ describe('<Modal />', () => {
       backdropSpan.simulate('click');
       assert.strictEqual(onBackdropClick.callCount, 0);
     });
+
+    // Test case for https://github.com/mui-org/material-ui/issues/12831
+    it('should unmount the children when starting open and closing immediately', () => {
+      function TestCase() {
+        const [open, setOpen] = React.useState(true);
+
+        React.useEffect(() => {
+          setOpen(false);
+        }, []);
+
+        return (
+          <Modal open={open}>
+            <Fade in={open}>
+              <div id="modal-body">hello</div>
+            </Fade>
+          </Modal>
+        );
+      }
+      render(<TestCase />);
+      expect(document.querySelector('#modal-body')).to.equal(null);
+    });
   });
 
   describe('render', () => {