From 01c40eae5509f1c150d79269a7a75c05112fa343 Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Tue, 31 May 2022 09:57:27 -0400 Subject: [PATCH] fix(react): present controller overlays in React 18 (#25361) Resolves #25247 --- .../components/createControllerComponent.tsx | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/react/src/components/createControllerComponent.tsx b/packages/react/src/components/createControllerComponent.tsx index 8078d00aab..4f3dc6d82c 100644 --- a/packages/react/src/components/createControllerComponent.tsx +++ b/packages/react/src/components/createControllerComponent.tsx @@ -1,7 +1,12 @@ import { OverlayEventDetail } from '@ionic/core/components'; import React from 'react'; -import { attachProps, dashToPascalCase, defineCustomElement, setRef } from './react-component-lib/utils'; +import { + attachProps, + dashToPascalCase, + defineCustomElement, + setRef, +} from './react-component-lib/utils'; interface OverlayBase extends HTMLElement { present: () => Promise; @@ -39,7 +44,7 @@ export const createControllerComponent = < class Overlay extends React.Component { overlay?: OverlayType; - isUnmounted = false; + willUnmount = false; constructor(props: Props) { super(props); @@ -51,6 +56,14 @@ export const createControllerComponent = < } async componentDidMount() { + /** + * Starting in React v18, strict mode will unmount and remount a component. + * See: https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors + * + * We need to reset this flag when the component is re-mounted so that + * overlay.present() will be called and the overlay will display. + */ + this.willUnmount = false; const { isOpen } = this.props; if (isOpen as boolean) { this.present(); @@ -58,7 +71,7 @@ export const createControllerComponent = < } componentWillUnmount() { - this.isUnmounted = true; + this.willUnmount = true; if (this.overlay) { this.overlay.dismiss(); } @@ -77,18 +90,17 @@ export const createControllerComponent = < if (this.props.onDidDismiss) { this.props.onDidDismiss(event); } - setRef(this.props.forwardedRef, null) + setRef(this.props.forwardedRef, null); } async present(prevProps?: Props) { - const { - isOpen, - onDidDismiss, - onDidPresent, - onWillDismiss, - onWillPresent, - ...cProps - } = this.props; + const { isOpen, onDidDismiss, onDidPresent, onWillDismiss, onWillPresent, ...cProps } = + this.props; + + if (this.overlay) { + this.overlay.remove(); + } + this.overlay = await controller.create({ ...(cProps as any), }); @@ -107,8 +119,8 @@ export const createControllerComponent = < ); // Check isOpen again since the value could have changed during the async call to controller.create // It's also possible for the component to have become unmounted. - if (this.props.isOpen === true && this.isUnmounted === false) { - setRef(this.props.forwardedRef, this.overlay) + if (this.props.isOpen === true && this.willUnmount === false) { + setRef(this.props.forwardedRef, this.overlay); await this.overlay.present(); } }