From d12f35705f2914aadaf11f8e4c8df626990b3716 Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Fri, 19 Jul 2024 14:53:31 -0400 Subject: [PATCH] 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. --- .../src/ReactRouter/IonReactRouter.tsx | 13 +++++-- .../src/ReactRouter/ReactRouterViewStack.tsx | 25 ++++++------- .../src/ReactRouter/StackManager.tsx | 35 ++++++++++++------- .../src/ReactRouter/utils/findRoutesNode.ts | 16 +++++++++ 4 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 packages/react-router/src/ReactRouter/utils/findRoutesNode.ts diff --git a/packages/react-router/src/ReactRouter/IonReactRouter.tsx b/packages/react-router/src/ReactRouter/IonReactRouter.tsx index 86da2bacd3..72c3de9181 100644 --- a/packages/react-router/src/ReactRouter/IonReactRouter.tsx +++ b/packages/react-router/src/ReactRouter/IonReactRouter.tsx @@ -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) => { return ( @@ -18,8 +21,12 @@ export const IonReactRouter = ({ children, ...rest }: PropsWithChildren) => { const location = useLocation(); + const navigationType = useNavigationType(); const historyListenHandler = useRef<(location: HistoryLocation, action: HistoryAction) => void>(); @@ -36,8 +43,8 @@ const IonInnerReactRouter = ({ children }: PropsWithChildren { - handleHistoryChange(location, 'POP'); // TODO @sean unsure about this - }, [location]); + handleHistoryChange(location, navigationType); + }, [location, navigationType]); return {children}; }; diff --git a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx index 5f99c0d839..34c31732db 100644 --- a/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx +++ b/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx @@ -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)} > - - {React.cloneElement(viewItem.reactElement, { - computedMatch: viewItem.routeData.match, - })} - + {React.cloneElement(viewItem.reactElement, { + computedMatch: viewItem.routeData.match, + })} ); diff --git a/packages/react-router/src/ReactRouter/StackManager.tsx b/packages/react-router/src/ReactRouter/StackManager.tsx index 708de65139..f67eb75afd 100644 --- a/packages/react-router/src/ReactRouter/StackManager.tsx +++ b/packages/react-router/src/ReactRouter/StackManager.tsx @@ -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 { - 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; + } } }); diff --git a/packages/react-router/src/ReactRouter/utils/findRoutesNode.ts b/packages/react-router/src/ReactRouter/utils/findRoutesNode.ts new file mode 100644 index 0000000000..684e7c0907 --- /dev/null +++ b/packages/react-router/src/ReactRouter/utils/findRoutesNode.ts @@ -0,0 +1,16 @@ +import React from "react"; +import { Routes } from "react-router"; + +export const findRoutesNode = (node: React.ReactNode) => { + // Finds the 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; +};