fix(react): IonNav apply properties to page components (#25603)

Resolves #25602
This commit is contained in:
Sean Perkins
2022-07-28 16:32:06 -04:00
committed by GitHub
parent ea830734c5
commit 61e4ffe47f
4 changed files with 127 additions and 74 deletions

View File

@ -4,27 +4,34 @@ import React, { useState } from 'react';
import { ReactDelegate } from '../../framework-delegate';
import { createReactComponent } from '../react-component-lib';
import { createForwardRef } from '../utils';
const IonNavInner = createReactComponent<
JSX.IonNav & { delegate: FrameworkDelegate },
HTMLIonNavElement
>('ion-nav', undefined, undefined, defineCustomElement);
export const IonNav: React.FC<JSX.IonNav> = ({ children, ...restOfProps }) => {
const [views, setViews] = useState<React.ReactPortal[]>([]);
type IonNavProps = JSX.IonNav & {
forwardedRef?: React.ForwardedRef<HTMLIonNavElement>;
};
const IonNavInternal: React.FC<IonNavProps> = ({ children, forwardedRef, ...restOfProps }) => {
const [views, setViews] = useState<React.ReactElement[]>([]);
/**
* Allows us to create React components that are rendered within
* the context of the IonNav component.
*/
const addView = (view: React.ReactPortal) => setViews([...views, view]);
const removeView = (view: React.ReactPortal) => setViews(views.filter((v) => v !== view));
const addView = (view: React.ReactElement) => setViews([...views, view]);
const removeView = (view: React.ReactElement) => setViews(views.filter((v) => v !== view));
const delegate = ReactDelegate(addView, removeView);
return (
<IonNavInner delegate={delegate} {...restOfProps}>
<IonNavInner delegate={delegate} ref={forwardedRef} {...restOfProps}>
{views}
</IonNavInner>
);
};
export const IonNav = createForwardRef<IonNavProps, HTMLIonNavElement>(IonNavInternal, 'IonNav');

View File

@ -1,15 +1,17 @@
import { FrameworkDelegate } from '@ionic/core/components';
import { createPortal } from 'react-dom';
type ReactComponent = (props?: any) => JSX.Element;
export const ReactDelegate = (
addView: (view: React.ReactPortal) => void,
removeView: (view: React.ReactPortal) => void
addView: (view: React.ReactElement) => void,
removeView: (view: React.ReactElement) => void
): FrameworkDelegate => {
let Component: React.ReactPortal;
const refMap = new WeakMap<ReactComponent, React.ReactElement>();
const attachViewToDom = async (
parentElement: HTMLElement,
component: () => JSX.Element,
component: ReactComponent,
propsOrDataObj?: any,
cssClasses?: string[]
): Promise<any> => {
@ -17,17 +19,20 @@ export const ReactDelegate = (
cssClasses && div.classList.add(...cssClasses);
parentElement.appendChild(div);
Component = createPortal(component(), div);
const componentWithProps = component(propsOrDataObj);
const hostComponent = createPortal(componentWithProps, div);
Component.props = propsOrDataObj;
refMap.set(component, hostComponent);
addView(Component);
addView(hostComponent);
return Promise.resolve(div);
};
const removeViewFromDom = (): Promise<void> => {
Component && removeView(Component);
const removeViewFromDom = (_container: any, component: ReactComponent): Promise<void> => {
const hostComponent = refMap.get(component);
hostComponent && removeView(hostComponent);
return Promise.resolve();
};