mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 18:17:31 +08:00
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:
@ -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>;
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
};
|
Reference in New Issue
Block a user