fix: mounting of pages

Notes: Push and Pop animations are not working. Swipe to go back isn't working. It isn't keeping the leaving view item in the DOM after pushing a new entering view item.
This commit is contained in:
Sean Perkins
2024-07-19 14:53:31 -04:00
parent cb8e54e9ac
commit d12f35705f
4 changed files with 61 additions and 28 deletions

View File

@ -2,7 +2,7 @@ import type { Action as HistoryAction, History, Location as HistoryLocation } fr
import type { PropsWithChildren } from 'react';
import React, { useEffect, useRef } from 'react';
import type { BrowserRouterProps } from 'react-router-dom';
import { BrowserRouter, useLocation } from 'react-router-dom';
import { BrowserRouter, useLocation, useNavigationType } from 'react-router-dom';
import { IonRouter } from './IonRouter';
@ -10,6 +10,9 @@ interface IonReactRouterProps extends BrowserRouterProps {
history?: History;
}
/**
* `IonReactRouter` is Ionic's routing container component. It is a wrapper around `BrowserRouter` from `react-router-dom`.
*/
export const IonReactRouter = ({ children, ...rest }: PropsWithChildren<IonReactRouterProps>) => {
return (
<BrowserRouter>
@ -18,8 +21,12 @@ export const IonReactRouter = ({ children, ...rest }: PropsWithChildren<IonReact
);
};
/**
* Browser router provides the context APIs for the hooks to work.
*/
const IonInnerReactRouter = ({ children }: PropsWithChildren<IonReactRouterProps>) => {
const location = useLocation();
const navigationType = useNavigationType();
const historyListenHandler = useRef<(location: HistoryLocation, action: HistoryAction) => void>();
@ -36,8 +43,8 @@ const IonInnerReactRouter = ({ children }: PropsWithChildren<IonReactRouterProps
};
useEffect(() => {
handleHistoryChange(location, 'POP'); // TODO @sean unsure about this
}, [location]);
handleHistoryChange(location, navigationType);
}, [location, navigationType]);
return <IonRouter registerHistoryListener={registerHistoryListener}>{children}</IonRouter>;
};

View File

@ -1,8 +1,9 @@
import type { RouteInfo, ViewItem } from '@ionic/react';
import { IonRoute, ViewLifeCycleManager, ViewStacks, generateId } from '@ionic/react';
import React from 'react';
import { Routes } from 'react-router';
import { Route } from 'react-router';
import { findRoutesNode } from './utils/findRoutesNode';
import { matchPath } from './utils/matchPath';
export class ReactRouterViewStack extends ViewStacks {
@ -45,12 +46,14 @@ export class ReactRouterViewStack extends ViewStacks {
const viewItems = this.getViewItemsForOutlet(outletId);
// Sync latest routes with viewItems
React.Children.forEach(ionRouterOutlet.props.children, (child: React.ReactElement) => {
const viewItem = viewItems.find((v) => {
return matchComponent(child, v.routeData.childProps.path || v.routeData.childProps.from);
});
if (viewItem) {
viewItem.reactElement = child;
React.Children.forEach(findRoutesNode(ionRouterOutlet.props.children), (child: React.ReactElement) => {
if (child.type === Route) {
const viewItem = viewItems.find((v) => {
return matchComponent(child, v.routeData.childProps.path || v.routeData.childProps.from);
});
if (viewItem) {
viewItem.reactElement = child;
}
}
});
@ -61,11 +64,9 @@ export class ReactRouterViewStack extends ViewStacks {
mount={viewItem.mount}
removeView={() => this.remove(viewItem)}
>
<Routes>
{React.cloneElement(viewItem.reactElement, {
computedMatch: viewItem.routeData.match,
})}
</Routes>
{React.cloneElement(viewItem.reactElement, {
computedMatch: viewItem.routeData.match,
})}
</ViewLifeCycleManager>
);

View File

@ -1,8 +1,10 @@
import type { RouteInfo, StackContextState, ViewItem } from '@ionic/react';
import { RouteManagerContext, StackContext, generateId, getConfig } from '@ionic/react';
import React from 'react';
import { Route } from 'react-router';
import { clonePageElement } from './clonePageElement';
import { findRoutesNode } from './utils/findRoutesNode';
import { matchPath } from './utils/matchPath';
// TODO(FW-2959): types
@ -113,9 +115,9 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
const enteringRoute = matchRoute(this.ionRouterOutlet?.props.children, routeInfo) as React.ReactElement;
if (enteringViewItem) {
enteringViewItem.reactElement = enteringRoute;
enteringViewItem.reactElement = enteringRoute.props.element;
} else if (enteringRoute) {
enteringViewItem = this.context.createViewItem(this.id, enteringRoute, routeInfo);
enteringViewItem = this.context.createViewItem(this.id, enteringRoute.props.element, routeInfo);
this.context.addViewItem(enteringViewItem);
}
@ -133,7 +135,7 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
* If the route data does not match the current path, the parent router outlet
* is attempting to transition and we cancel the operation.
*/
if (enteringViewItem.routeData.match.url !== routeInfo.pathname) {
if (enteringViewItem.routeData.match.pathname !== routeInfo.pathname) {
return;
}
}
@ -432,13 +434,18 @@ export default StackManager;
function matchRoute(node: React.ReactNode, routeInfo: RouteInfo) {
let matchedNode: React.ReactNode;
React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => {
const match = matchPath({
pathname: routeInfo.pathname,
componentProps: child.props,
});
if (match) {
matchedNode = child;
const routesNode = findRoutesNode(node) ?? node;
React.Children.forEach(routesNode, (child: React.ReactElement) => {
if (child.type === Route) {
const match = matchPath({
pathname: routeInfo.pathname,
componentProps: child.props,
});
if (match) {
matchedNode = child;
}
}
});
@ -447,9 +454,11 @@ function matchRoute(node: React.ReactNode, routeInfo: RouteInfo) {
}
// If we haven't found a node
// try to find one that doesn't have a path or from prop, that will be our not found route
React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => {
if (!(child.props.path || child.props.from)) {
matchedNode = child;
React.Children.forEach(routesNode, (child: React.ReactElement) => {
if (child.type === Route) {
if (!(child.props.path || child.props.from)) {
matchedNode = child;
}
}
});

View File

@ -0,0 +1,16 @@
import React from "react";
import { Routes } from "react-router";
export const findRoutesNode = (node: React.ReactNode) => {
// Finds the <Routes /> component node
let routesNode: React.ReactNode;
React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => {
if (child.type === Routes) {
routesNode = child;
}
});
if (routesNode) {
return (routesNode as React.ReactElement).props.children;
}
return undefined;
};