From eef55bb0072a9e54b1fd7d1c8c69e7fd43b2a5c5 Mon Sep 17 00:00:00 2001 From: Ely Lucas Date: Wed, 27 Nov 2019 16:08:56 -0700 Subject: [PATCH] fix(react): fix refs for controllers, overlays, ionpage, and ionrouteroutlet, fixes #19924 (#20012) --- .../src/ReactRouter/StackManager.tsx | 6 +++- packages/react/src/components/IonPage.tsx | 27 +++++++++++---- .../react/src/components/IonRouterOutlet.tsx | 2 +- .../react/src/components/IonicReactProps.ts | 1 + .../components/createControllerComponent.tsx | 27 ++++++++++++--- .../src/components/createOverlayComponent.tsx | 33 +++++++++++++++---- 6 files changed, 78 insertions(+), 18 deletions(-) diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 6cccf00fbd7..f9d85c3168b 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -87,6 +87,10 @@ export class StackManager extends React.Component class IonPageInternal extends React.Component & IonicReactProps> { +interface IonPageProps extends IonicReactProps { +} + +interface IonPageInternalProps extends IonPageProps { + forwardedRef?: React.RefObject; +} + +class IonPageInternal extends React.Component { context!: React.ContextType; - ref = React.createRef(); + ref: React.RefObject;// React.createRef(); + + constructor(props: IonPageInternalProps) { + super(props); + this.ref = this.props.forwardedRef || React.createRef() + } componentDidMount() { - if (this.context && this.ref.current) { + if (this.context && this.ref && this.ref.current) { if (this.context.hasIonicRouter()) { this.context.registerIonPage(this.ref.current); } - } + } } render() { - const { className, children, ...props } = this.props; + const { className, children, forwardedRef, ...props } = this.props; return (
@@ -33,4 +46,6 @@ export const IonPage = /*@__PURE__*/(() => class IonPageInternal extends React.C static get contextType() { return NavContext; } -})(); +}; + +export const IonPage = createForwardRef(IonPageInternal, 'IonPage'); diff --git a/packages/react/src/components/IonRouterOutlet.tsx b/packages/react/src/components/IonRouterOutlet.tsx index 0c31bf3cc0c..63d4a2e44dd 100644 --- a/packages/react/src/components/IonRouterOutlet.tsx +++ b/packages/react/src/components/IonRouterOutlet.tsx @@ -13,7 +13,7 @@ type Props = LocalJSX.IonRouterOutlet & { }; type InternalProps = Props & { - forwardedRef: any; + forwardedRef?: React.RefObject; }; const IonRouterOutletContainer = /*@__PURE__*/(() => class extends React.Component { diff --git a/packages/react/src/components/IonicReactProps.ts b/packages/react/src/components/IonicReactProps.ts index a18e0b7f166..2ea45b08b37 100644 --- a/packages/react/src/components/IonicReactProps.ts +++ b/packages/react/src/components/IonicReactProps.ts @@ -1,5 +1,6 @@ export interface IonicReactProps { class?: string; + className?: string; style?: {[key: string]: any }; } diff --git a/packages/react/src/components/createControllerComponent.tsx b/packages/react/src/components/createControllerComponent.tsx index 62def561c7a..ef8dcec68ea 100644 --- a/packages/react/src/components/createControllerComponent.tsx +++ b/packages/react/src/components/createControllerComponent.tsx @@ -19,14 +19,17 @@ export const createControllerComponent = { const dismissEventName = `on${displayName}DidDismiss`; - type Props = OptionsType & ReactControllerProps; + type Props = OptionsType & ReactControllerProps & { + forwardedRef?: React.RefObject + }; - return class extends React.Component { + class Overlay extends React.Component { overlay?: OverlayType; isUnmounted = false; constructor(props: Props) { super(props); + this.handleDismiss = this.handleDismiss.bind(this); } static get displayName() { @@ -54,18 +57,30 @@ export const createControllerComponent = >) { + if (this.props.onDidDismiss) { + this.props.onDidDismiss(event); + } + if (this.props.forwardedRef) { + (this.props.forwardedRef as any).current = undefined; + } + } + async present(prevProps?: Props) { const { isOpen, onDidDismiss, ...cProps } = this.props; this.overlay = await controller.create({ ...cProps as any }); attachProps(this.overlay, { - [dismissEventName]: onDidDismiss + [dismissEventName]: this.handleDismiss }, prevProps); // 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) { - await this.overlay.present(); + if (this.props.forwardedRef) { + (this.props.forwardedRef as any).current = this.overlay; + } + await this.overlay.present(); } } @@ -73,4 +88,8 @@ export const createControllerComponent = ((props, ref) => { + return + }) }; diff --git a/packages/react/src/components/createOverlayComponent.tsx b/packages/react/src/components/createOverlayComponent.tsx index 113736e2cfd..7560904afdb 100644 --- a/packages/react/src/components/createOverlayComponent.tsx +++ b/packages/react/src/components/createOverlayComponent.tsx @@ -15,21 +15,24 @@ export interface ReactOverlayProps { onDidDismiss?: (event: CustomEvent) => void; } -export const createOverlayComponent = ( +export const createOverlayComponent = ( displayName: string, controller: { create: (options: any) => Promise } ) => { const dismissEventName = `on${displayName}DidDismiss`; - type Props = T & ReactOverlayProps; + type Props = OverlayComponent & ReactOverlayProps & { + forwardedRef?: React.RefObject + }; - return class extends React.Component { + class Overlay extends React.Component { overlay?: OverlayType; el: HTMLDivElement; constructor(props: Props) { super(props); this.el = document.createElement('div'); + this.handleDismiss = this.handleDismiss.bind(this); } static get displayName() { @@ -46,6 +49,15 @@ export const createOverlayComponent = >) { + if (this.props.onDidDismiss) { + this.props.onDidDismiss(event); + } + if (this.props.forwardedRef) { + (this.props.forwardedRef as any).current = undefined; + } + } + async componentDidUpdate(prevProps: Props) { if (prevProps.isOpen !== this.props.isOpen && this.props.isOpen === true) { this.present(prevProps); @@ -56,10 +68,11 @@ export const createOverlayComponent = { return; }, ...cProps } = this.props; + const { children, isOpen, onDidDismiss, ...cProps } = this.props; const elementProps = { ...cProps, - [dismissEventName]: onDidDismiss + ref: this.props.forwardedRef, + [dismissEventName]: this.handleDismiss }; const overlay = this.overlay = await controller.create({ @@ -68,6 +81,10 @@ export const createOverlayComponent = ((props, ref) => { + return + }) };