React: Can not prevent portal unmounting when using ReactDOM.createPortal

Created on 26 Sep 2017  Â·  11Comments  Â·  Source: facebook/react

Previous comment https://github.com/facebook/react/issues/10143#issuecomment-331852434

When we use unstable_renderIntoContainer implements portal, we can control when the portal unmount, this seems impossible when use createPortal, because when parent component unmount, portal will be unmount by React itself.

Reconciler Discussion

Most helpful comment

So, any solutions here? It's still a problem for me too... Cannot figure out how to prevent portal unmounts...

All 11 comments

Unmounting on parent unmount looks like a feature, not a bug to me.

Perhaps doing something similar to what TransitionGroup does can work for your use case? When a child element is removed from TransitionGroup's children list it's not actually yet removed from the TransitionGroup's render result (so React doesn't unmount it), it just clones the last used props and sets a prop (in) to false. Once the child is ready to be removed, it calls the onExited prop callback.

+1

When a child element is removed from TransitionGroup's children list it's not actually yet removed from the TransitionGroup's render result (so React doesn't unmount it), it just clones the last used props and sets a prop (in) to false.

Actually, we cannot, we are maintaining a common UI component rc-dialog. We cannot prohibit other developers unmount our dialog, this is why it is a problem to us(And we know that it may not be a problem to others).

This is a problem for us too.
~The only work-around I can think of is to cache the dom and restore it after unmount and hope it doesn't flicker, which is pretty horrible.~

Having the same issue, and the only solution i've found is to not use createPortal :(

Hey y'all. I don't think the old behavior was actually supported, even if it sort of worked most of the time. The subtree component may have stayed rendered but it would be in an orphaned state, and likely to break if any update occurred on it, because the assumption was that the subtree is always actually parented. I've personally run into bugs where I've had the parent component unmount and then break something in the portal component on the next update cycle.

The best work-around I can think of is to do a ReactDOM.render in a asap to escape the react lifecycle stack trace.. then you have control over when the element is unmounted. we'll also need some magic to copy the context through, but I can't think of any other disadvantages?

How would the api for this look like? Would this be too inflexible?

ReactDOM.createPortal(children, element, (actualUnmount) => actualUnmount());

It seems like the use case was not really supported in the past either and led to errors.

So I think we can’t say this is a “missing feature” of the portal API. It just isn’t as easily broken as the existing old API.

If you have specific API suggestions around this problem please create an RFC: https://github.com/reactjs/rfcs

Thanks!

So, any solutions here? It's still a problem for me too... Cannot figure out how to prevent portal unmounts...

You usually want to prevent a portal parent from unmounting in a case, when you have some kind of a dropdown component, which should open a modal on click inside. Because it's a dropdown, it hides on click inside, and that triggers a modal to unmount too. We've fixed this problem by rendering hidden content of a dropdown with a display: none style. Maybe it'll help somebody.

You usually want to prevent a portal parent from unmounting in a case, when you have some kind of a dropdown component, which should open a modal on click inside. Because it's a dropdown, it hides on click inside, and that triggers a modal to unmount too. We've fixed this problem by rendering hidden content of a dropdown with a display: none style. Maybe it'll help somebody.

This was exactly my issue but I handle it like this, I added a preventAutoClose prop to my dropdown items as when I click on the item I had an auto close function that ran on mouseup and a timeout of 50ms to auto close the dropdown when I clicked on it.

this was my solution:

const handleClick = () => {
    setTimeout(() => close && close(), 50);
};

useEffect(() => {
    if (preventCloseClick) {
        document.addEventListener('mouseup', handleClick);
    }

    return () => {
        if (preventCloseClick) {
            document.removeEventListener('mouseup', handleClick);
        }
    };
}, []);

so I just add preventCloseClick={true} to my <DropdownItem /> and it no longer unmounts unexpected..

Was this page helpful?
0 / 5 - 0 ratings