fix(react): present controller overlays in React 18 (#25361)

Resolves #25247
This commit is contained in:
Sean Perkins
2022-05-31 09:57:27 -04:00
committed by GitHub
parent 982909017b
commit 01c40eae55

View File

@ -1,7 +1,12 @@
import { OverlayEventDetail } from '@ionic/core/components'; import { OverlayEventDetail } from '@ionic/core/components';
import React from 'react'; 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 { interface OverlayBase extends HTMLElement {
present: () => Promise<void>; present: () => Promise<void>;
@ -39,7 +44,7 @@ export const createControllerComponent = <
class Overlay extends React.Component<Props> { class Overlay extends React.Component<Props> {
overlay?: OverlayType; overlay?: OverlayType;
isUnmounted = false; willUnmount = false;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
@ -51,6 +56,14 @@ export const createControllerComponent = <
} }
async componentDidMount() { 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; const { isOpen } = this.props;
if (isOpen as boolean) { if (isOpen as boolean) {
this.present(); this.present();
@ -58,7 +71,7 @@ export const createControllerComponent = <
} }
componentWillUnmount() { componentWillUnmount() {
this.isUnmounted = true; this.willUnmount = true;
if (this.overlay) { if (this.overlay) {
this.overlay.dismiss(); this.overlay.dismiss();
} }
@ -77,18 +90,17 @@ export const createControllerComponent = <
if (this.props.onDidDismiss) { if (this.props.onDidDismiss) {
this.props.onDidDismiss(event); this.props.onDidDismiss(event);
} }
setRef(this.props.forwardedRef, null) setRef(this.props.forwardedRef, null);
} }
async present(prevProps?: Props) { async present(prevProps?: Props) {
const { const { isOpen, onDidDismiss, onDidPresent, onWillDismiss, onWillPresent, ...cProps } =
isOpen, this.props;
onDidDismiss,
onDidPresent, if (this.overlay) {
onWillDismiss, this.overlay.remove();
onWillPresent, }
...cProps
} = this.props;
this.overlay = await controller.create({ this.overlay = await controller.create({
...(cProps as any), ...(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 // 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. // It's also possible for the component to have become unmounted.
if (this.props.isOpen === true && this.isUnmounted === false) { if (this.props.isOpen === true && this.willUnmount === false) {
setRef(this.props.forwardedRef, this.overlay) setRef(this.props.forwardedRef, this.overlay);
await this.overlay.present(); await this.overlay.present();
} }
} }