fix(react): first render performance improvements

This commit is contained in:
Ely Lucas
2019-12-09 14:36:47 -07:00
committed by GitHub
parent 684293ddbf
commit 1c7d1e5cf1
11 changed files with 160 additions and 174 deletions

View File

@ -176,34 +176,30 @@ will match the following tab:
### React ### React
```tsx ```tsx
import React from 'react';
import { IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel, IonBadge } from '@ionic/react';
export const TabsExample: React.FC = () => ( export const TabsExample: React.FC = () => (
<IonTabs> <IonTabs>
<IonRouterOutlet>
<Redirect exact path="/" to="/tabs/schedule" />
{/*
Using the render method prop cuts down the number of renders your components will have due to route changes.
Use the component prop when your component depends on the RouterComponentProps passed in automatically.
*/}
<Route path="/tabs/schedule" render={() => <SchedulePage />} exact={true} />
<Route path="/tabs/speakers" render={() => <SpeakerList />} exact={true} />
<Route path="/tabs/map" render={() => <MapView />} exact={true} />
<Route path="/tabs/about" render={() => <About />} exact={true} />
</IonRouterOutlet>
<IonTabBar slot="bottom"> <IonTabBar slot="bottom">
<IonTabButton tab="schedule" href="/tabs/schedule"> <IonTabButton tab="schedule">
<IonIcon icon={calendar} /> <IonIcon name="calendar" />
<IonLabel>Schedule</IonLabel> <IonLabel>Schedule</IonLabel>
<IonBadge>6</IonBadge>
</IonTabButton> </IonTabButton>
<IonTabButton tab="speakers" href="/tabs/speakers">
<IonIcon icon={contacts} /> <IonTabButton tab="speakers">
<IonIcon name="contacts" />
<IonLabel>Speakers</IonLabel> <IonLabel>Speakers</IonLabel>
</IonTabButton> </IonTabButton>
<IonTabButton tab="map" href="/tabs/map">
<IonIcon icon={map} /> <IonTabButton tab="map">
<IonIcon name="map" />
<IonLabel>Map</IonLabel> <IonLabel>Map</IonLabel>
</IonTabButton> </IonTabButton>
<IonTabButton tab="about" href="/tabs/about">
<IonIcon icon={informationCircle} /> <IonTabButton tab="about">
<IonIcon name="information-circle" />
<IonLabel>About</IonLabel> <IonLabel>About</IonLabel>
</IonTabButton> </IonTabButton>
</IonTabBar> </IonTabBar>

View File

@ -0,0 +1 @@
export type IonRouteAction = 'push' | 'replace' | 'pop';

View File

@ -4,11 +4,12 @@ import { Location as HistoryLocation, UnregisterCallback } from 'history';
import React from 'react'; import React from 'react';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
import { IonRouteAction } from './IonRouteAction';
import { StackManager } from './StackManager'; import { StackManager } from './StackManager';
interface NavManagerProps extends RouteComponentProps { interface NavManagerProps extends RouteComponentProps {
onNavigateBack: (defaultHref?: string) => void; onNavigateBack: (defaultHref?: string) => void;
onNavigate: (type: 'push' | 'replace', path: string, state?: any) => void; onNavigate: (type: 'push' | 'replace' | 'pop', path: string, state?: any) => void;
} }
export class NavManager extends React.Component<NavManagerProps, NavContextState> { export class NavManager extends React.Component<NavManagerProps, NavContextState> {
@ -24,7 +25,7 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
getStackManager: this.getStackManager.bind(this), getStackManager: this.getStackManager.bind(this),
getPageManager: this.getPageManager.bind(this), getPageManager: this.getPageManager.bind(this),
currentPath: this.props.location.pathname, currentPath: this.props.location.pathname,
registerIonPage: () => { return; }, // overridden in View for each IonPage registerIonPage: () => { return; } // overridden in View for each IonPage
}; };
this.listenUnregisterCallback = this.props.history.listen((location: HistoryLocation) => { this.listenUnregisterCallback = this.props.history.listen((location: HistoryLocation) => {
@ -52,8 +53,8 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
this.props.onNavigateBack(defaultHref); this.props.onNavigateBack(defaultHref);
} }
navigate(path: string, direction?: RouterDirection | 'none', type: 'push' | 'replace' = 'push') { navigate(path: string, direction?: RouterDirection | 'none', ionRouteAction: IonRouteAction = 'push') {
this.props.onNavigate(type, path, direction); this.props.onNavigate(ionRouteAction, path, direction);
} }
getPageManager() { getPageManager() {

View File

@ -7,6 +7,7 @@ import { RouteComponentProps, matchPath, withRouter } from 'react-router-dom';
import { generateId, isDevMode } from '../utils'; import { generateId, isDevMode } from '../utils';
import { LocationHistory } from '../utils/LocationHistory'; import { LocationHistory } from '../utils/LocationHistory';
import { IonRouteAction } from './IonRouteAction';
import { IonRouteData } from './IonRouteData'; import { IonRouteData } from './IonRouteData';
import { NavManager } from './NavManager'; import { NavManager } from './NavManager';
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext'; import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
@ -15,15 +16,19 @@ import { ViewStack, ViewStacks } from './ViewStacks';
interface RouteManagerState extends RouteManagerContextState { interface RouteManagerState extends RouteManagerContextState {
location?: HistoryLocation; location?: HistoryLocation;
action?: HistoryAction; action?: IonRouteAction;
} }
class RouteManager extends React.Component<RouteComponentProps, RouteManagerState> { class RouteManager extends React.Component<RouteComponentProps, RouteManagerState> {
listenUnregisterCallback: UnregisterCallback | undefined; listenUnregisterCallback: UnregisterCallback | undefined;
activeIonPageId?: string; activeIonPageId?: string;
currentDirection?: RouterDirection; currentIonRouteAction?: IonRouteAction;
currentRouteDirection?: RouterDirection;
locationHistory = new LocationHistory(); locationHistory = new LocationHistory();
routes: { [key: string]: any; } = {}; routes: { [key: string]: React.ReactElement<any>; } = {};
ionPageElements: { [key: string]: HTMLElement; } = {};
routerOutlets: { [key: string]: HTMLIonRouterOutletElement; } = {};
firstRender = true;
constructor(props: RouteComponentProps) { constructor(props: RouteComponentProps) {
super(props); super(props);
@ -52,7 +57,8 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
componentDidUpdate(_prevProps: RouteComponentProps, prevState: RouteManagerState) { componentDidUpdate(_prevProps: RouteComponentProps, prevState: RouteManagerState) {
// Trigger a page change if the location or action is different // Trigger a page change if the location or action is different
if (this.state.location && prevState.location !== this.state.location || prevState.action !== this.state.action) { if (this.state.location && prevState.location !== this.state.location || prevState.action !== this.state.action) {
this.setActiveView(this.state.location!, this.state.action!); const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
this.setActiveView(this.state.location!, this.state.action!, viewStacks);
} }
} }
@ -71,10 +77,10 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
const { view } = viewStacks.findViewInfoById(viewId); const { view } = viewStacks.findViewInfoById(viewId);
if (view) { if (view) {
view.show = false; view.show = false;
view.ionPageElement = undefined;
view.isIonRoute = false; view.isIonRoute = false;
view.prevId = undefined; view.prevId = undefined;
view.key = generateId(); view.key = generateId();
delete this.ionPageElements[view.id];
this.setState({ this.setState({
viewStacks viewStacks
}); });
@ -82,23 +88,29 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
} }
historyChange(location: HistoryLocation, action: HistoryAction) { historyChange(location: HistoryLocation, action: HistoryAction) {
location.state = location.state || { direction: this.currentDirection }; const ionRouteAction = this.currentIonRouteAction === 'pop' ? 'pop' : action.toLowerCase();
this.currentDirection = undefined; let direction = this.currentRouteDirection;
if (action === 'PUSH') {
if (ionRouteAction === 'push') {
this.locationHistory.add(location); this.locationHistory.add(location);
} else if ((action === 'REPLACE' && location.state.direction === 'back') || action === 'POP') { } else if (ionRouteAction === 'pop') {
this.locationHistory.pop(); this.locationHistory.pop();
} else { direction = direction || 'back';
} else if (ionRouteAction === 'replace') {
this.locationHistory.replace(location); this.locationHistory.replace(location);
} direction = 'none';
this.setState({
location,
action
});
} }
setActiveView(location: HistoryLocation, action: HistoryAction) { location.state = location.state || { direction };
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks); this.setState({
location,
action: ionRouteAction as IonRouteAction
});
this.currentRouteDirection = undefined;
this.currentIonRouteAction = undefined;
}
setActiveView(location: HistoryLocation, action: IonRouteAction, viewStacks: ViewStacks) {
let direction: RouterDirection | undefined = (location.state && location.state.direction) || 'forward'; let direction: RouterDirection | undefined = (location.state && location.state.direction) || 'forward';
let leavingView: ViewItem | undefined; let leavingView: ViewItem | undefined;
const viewStackKeys = viewStacks.getKeys(); const viewStackKeys = viewStacks.getKeys();
@ -122,25 +134,18 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
this.activeIonPageId = enteringView.id; this.activeIonPageId = enteringView.id;
if (leavingView) { if (leavingView) {
if (direction === 'forward') { if (action === 'push' && direction === 'forward') {
if (action === 'PUSH') {
/** /**
* If the page is being pushed into the stack by another view, * If the page is being pushed into the stack by another view,
* record the view that originally directed to the new view for back button purposes. * record the view that originally directed to the new view for back button purposes.
*/ */
enteringView.prevId = leavingView.id; enteringView.prevId = leavingView.id;
} else if (action === 'POP') { } else if (action === 'pop' || action === 'replace') {
direction = leavingView.prevId === enteringView.id ? 'back' : 'none';
} else {
direction = direction || 'back';
leavingView.mount = false;
}
}
if (direction === 'back' || action === 'REPLACE') {
leavingView.mount = false; leavingView.mount = false;
this.removeOrphanedViews(enteringView, enteringViewStack); this.removeOrphanedViews(enteringView, enteringViewStack);
} }
leavingViewHtml = enteringView.id === leavingView.id ? leavingView.ionPageElement!.outerHTML : undefined;
leavingViewHtml = enteringView.id === leavingView.id ? this.ionPageElements[leavingView.id].outerHTML : undefined;
} else { } else {
// If there is not a leavingView, then we shouldn't provide a direction // If there is not a leavingView, then we shouldn't provide a direction
direction = undefined; direction = undefined;
@ -167,16 +172,17 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
if (shouldTransitionPage) { if (shouldTransitionPage) {
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId); const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
if (enteringView && viewStack) { if (enteringView && viewStack) {
const enteringEl = enteringView.ionPageElement ? enteringView.ionPageElement : undefined; const enteringEl = this.ionPageElements[enteringView.id];
const leavingEl = leavingView && leavingView.ionPageElement ? leavingView.ionPageElement : undefined; const leavingEl = leavingView && this.ionPageElements[leavingView.id];
if (enteringEl) { if (enteringEl) {
// Don't animate from an empty view // Don't animate from an empty view
const navDirection = leavingEl && leavingEl.innerHTML === '' ? undefined : direction === 'none' ? undefined : direction; const navDirection = leavingEl && leavingEl.innerHTML === '' ? undefined : direction === 'none' ? undefined : direction;
const shouldGoBack = !!enteringView.prevId; const shouldGoBack = !!enteringView.prevId;
const routerOutlet = this.routerOutlets[viewStack.id];
this.commitView( this.commitView(
enteringEl!, enteringEl!,
leavingEl!, leavingEl!,
viewStack.routerOutlet, routerOutlet,
navDirection, navDirection,
shouldGoBack, shouldGoBack,
leavingViewHtml); leavingViewHtml);
@ -211,12 +217,13 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
this.removeOrphanedViews(v, viewStack); this.removeOrphanedViews(v, viewStack);
// If view is not currently visible, go ahead and remove it from DOM // If view is not currently visible, go ahead and remove it from DOM
if (v.ionPageElement!.classList.contains('ion-page-hidden')) { const page = this.ionPageElements[v.id];
if (page.classList.contains('ion-page-hidden')) {
v.show = false; v.show = false;
v.ionPageElement = undefined;
v.isIonRoute = false; v.isIonRoute = false;
v.prevId = undefined; v.prevId = undefined;
v.key = generateId(); v.key = generateId();
delete this.ionPageElements[v.id];
} }
v.mount = false; v.mount = false;
} }
@ -270,9 +277,9 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks); const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
const newStack: ViewStack = { const newStack: ViewStack = {
id: stack, id: stack,
views: stackItems, views: stackItems
routerOutlet
}; };
this.routerOutlets[stack] = routerOutlet;
if (activeId) { if (activeId) {
this.activeIonPageId = activeId; this.activeIonPageId = activeId;
} }
@ -286,18 +293,6 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
} }
async setupRouterOutlet(routerOutlet: HTMLIonRouterOutletElement) { async setupRouterOutlet(routerOutlet: HTMLIonRouterOutletElement) {
const waitUntilReady = async () => {
if (routerOutlet.componentOnReady) {
routerOutlet.dispatchEvent(new Event('routerOutletReady'));
return;
} else {
setTimeout(() => {
waitUntilReady();
}, 0);
}
};
await waitUntilReady();
const canStart = () => { const canStart = () => {
const config = getConfig(); const config = getConfig();
@ -329,20 +324,13 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
} }
syncView(page: HTMLElement, viewId: string) { syncView(page: HTMLElement, viewId: string) {
this.setState(state => { const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
const { view } = viewStacks.findViewInfoById(viewId); const { view } = viewStacks.findViewInfoById(viewId);
if (view) {
view!.ionPageElement = page; view.isIonRoute = true;
view!.isIonRoute = true; this.ionPageElements[view.id] = page;
this.setActiveView(this.state.location || this.props.location, this.state.action!, viewStacks);
return { }
viewStacks
};
}, () => {
this.setActiveView(this.state.location || this.props.location, this.state.action!);
});
} }
syncRoute(_id: string, routerOutlet: any) { syncRoute(_id: string, routerOutlet: any) {
@ -359,6 +347,11 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
} }
private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement, direction?: NavDirection, showGoBack?: boolean, leavingViewHtml?: string) { private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement, direction?: NavDirection, showGoBack?: boolean, leavingViewHtml?: string) {
if (!this.firstRender) {
if (!('componentOnReady' in ionRouterOutlet)) {
await waitUntilRouterOutletReady(ionRouterOutlet);
}
if ((enteringEl === leavingEl) && direction && leavingViewHtml) { if ((enteringEl === leavingEl) && direction && leavingViewHtml) {
// If a page is transitioning to another version of itself // If a page is transitioning to another version of itself
@ -388,14 +381,28 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
leavingEl.classList.add('ion-page-hidden'); leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true'); leavingEl.setAttribute('aria-hidden', 'true');
} }
} else {
enteringEl.classList.remove('ion-page-invisible');
enteringEl.style.zIndex = '101';
this.firstRender = false;
}
} }
handleNavigate(type: 'push' | 'replace', path: string, direction?: RouterDirection) { handleNavigate(ionRouteAction: IonRouteAction, path: string, direction?: RouterDirection) {
this.currentDirection = direction; this.currentIonRouteAction = ionRouteAction;
if (type === 'push') { switch (ionRouteAction) {
case 'push':
this.currentRouteDirection = direction;
this.props.history.push(path); this.props.history.push(path);
} else { break;
case 'pop':
this.currentRouteDirection = direction || 'back';
this.props.history.replace(path); this.props.history.replace(path);
break;
case 'replace':
this.currentRouteDirection = 'none';
this.props.history.replace(path);
break;
} }
} }
@ -405,26 +412,26 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
if (leavingView.id === leavingView.prevId) { if (leavingView.id === leavingView.prevId) {
const previousLocation = this.locationHistory.previous(); const previousLocation = this.locationHistory.previous();
if (previousLocation) { if (previousLocation) {
this.handleNavigate('replace', previousLocation.pathname + previousLocation.search, 'back'); this.handleNavigate('pop', previousLocation.pathname + previousLocation.search);
} else { } else {
defaultHref && this.handleNavigate('replace', defaultHref, 'back'); defaultHref && this.handleNavigate('pop', defaultHref);
} }
} else { } else {
const { view: enteringView } = this.state.viewStacks.findViewInfoById(leavingView.prevId); const { view: enteringView } = this.state.viewStacks.findViewInfoById(leavingView.prevId);
if (enteringView) { if (enteringView) {
const lastLocation = this.locationHistory.findLastLocationByUrl(enteringView.routeData.match!.url); const lastLocation = this.locationHistory.findLastLocationByUrl(enteringView.routeData.match!.url);
if (lastLocation) { if (lastLocation) {
this.handleNavigate('replace', lastLocation.pathname + lastLocation.search, 'back'); this.handleNavigate('pop', lastLocation.pathname + lastLocation.search);
} else { } else {
this.handleNavigate('replace', enteringView.routeData.match!.url, 'back'); this.handleNavigate('pop', enteringView.routeData.match!.url);
} }
} else { } else {
const currentLocation = this.locationHistory.previous(); const currentLocation = this.locationHistory.previous();
if (currentLocation) { if (currentLocation) {
this.handleNavigate('replace', currentLocation.pathname + currentLocation.search, 'back'); this.handleNavigate('pop', currentLocation.pathname + currentLocation.search);
} else { } else {
if (defaultHref) { if (defaultHref) {
this.handleNavigate('replace', defaultHref, 'back'); this.handleNavigate('pop', defaultHref);
} }
} }
} }
@ -464,5 +471,15 @@ function clonePageElement(leavingViewHtml: string) {
return newEl.firstChild as HTMLElement; return newEl.firstChild as HTMLElement;
} }
async function waitUntilRouterOutletReady(ionRouterOutlet: HTMLIonRouterElement) {
if ('componentOnReady' in ionRouterOutlet) {
return;
} else {
setTimeout(() => {
waitUntilRouterOutletReady(ionRouterOutlet);
}, 0);
}
}
export const RouteManagerWithRouter = withRouter(RouteManager); export const RouteManagerWithRouter = withRouter(RouteManager);
RouteManagerWithRouter.displayName = 'RouteManager'; RouteManagerWithRouter.displayName = 'RouteManager';

View File

@ -13,13 +13,10 @@ interface StackManagerProps {
children?: React.ReactNode; children?: React.ReactNode;
} }
interface StackManagerState { interface StackManagerState { }
routerOutletReady: boolean;
}
class StackManagerInner extends React.Component<StackManagerProps, StackManagerState> { class StackManagerInner extends React.Component<StackManagerProps, StackManagerState> {
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef(); routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
id: string; id: string;
constructor(props: StackManagerProps) { constructor(props: StackManagerProps) {
@ -27,18 +24,11 @@ class StackManagerInner extends React.Component<StackManagerProps, StackManagerS
this.id = this.props.id || generateId(); this.id = this.props.id || generateId();
this.handleViewSync = this.handleViewSync.bind(this); this.handleViewSync = this.handleViewSync.bind(this);
this.handleHideView = this.handleHideView.bind(this); this.handleHideView = this.handleHideView.bind(this);
this.state = { this.state = {};
routerOutletReady: false
};
} }
componentDidMount() { componentDidMount() {
this.props.routeManager.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!); this.props.routeManager.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!);
this.routerOutletEl.current!.addEventListener('routerOutletReady', () => {
this.setState({
routerOutletReady: true
});
});
} }
static getDerivedStateFromProps(props: StackManagerProps, state: StackManagerState) { static getDerivedStateFromProps(props: StackManagerProps, state: StackManagerState) {
@ -70,9 +60,7 @@ class StackManagerInner extends React.Component<StackManagerProps, StackManagerS
const viewStack = routeManager.viewStacks.get(this.id); const viewStack = routeManager.viewStacks.get(this.id);
const views = (viewStack || { views: [] }).views.filter(x => x.show); const views = (viewStack || { views: [] }).views.filter(x => x.show);
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement; const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
const { routerOutletReady } = this.state; const childElements = views.map(view => {
const childElements = routerOutletReady ? views.map(view => {
const route = routeManager.getRoute(view.routeId); const route = routeManager.getRoute(view.routeId);
return ( return (
<ViewTransitionManager <ViewTransitionManager
@ -90,7 +78,7 @@ class StackManagerInner extends React.Component<StackManagerProps, StackManagerS
</View> </View>
</ViewTransitionManager> </ViewTransitionManager>
); );
}) : <div></div>; });
const elementProps: any = { const elementProps: any = {
ref: this.routerOutletEl ref: this.routerOutletEl

View File

@ -1,6 +1,5 @@
import { IonLifeCycleContext, NavContext } from '@ionic/react'; import { IonLifeCycleContext, NavContext } from '@ionic/react';
import React from 'react'; import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { isDevMode } from '../utils'; import { isDevMode } from '../utils';
@ -20,20 +19,6 @@ export class View extends React.Component<ViewProps, {}> {
context!: React.ContextType<typeof IonLifeCycleContext>; context!: React.ContextType<typeof IonLifeCycleContext>;
ionPage?: HTMLElement; ionPage?: HTMLElement;
componentDidMount() {
/**
* If we can tell if view is a redirect, hide it so it will work again in future
*/
const { view, route } = this.props;
if (route.type === Redirect) {
this.props.onHideView(view.id);
} else if (route.type === Route && route.props.render) {
if (route.props.render().type === Redirect) {
this.props.onHideView(view.id);
}
}
}
componentWillUnmount() { componentWillUnmount() {
if (this.ionPage) { if (this.ionPage) {
this.ionPage.removeEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this)); this.ionPage.removeEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));

View File

@ -5,10 +5,7 @@ export interface ViewItem<RouteData = any> {
key: string; key: string;
routeId: string; routeId: string;
/** The <Route /> or <Redirect /> component associated with the view */
// route: React.ReactElement<any>;
/** The reference to the <IonPage /> element. */
ionPageElement?: HTMLElement;
/** The routeData for the view. */ /** The routeData for the view. */
routeData: RouteData; routeData: RouteData;
/** Used to track which page pushed the page into view. Used for back button purposes. */ /** Used to track which page pushed the page into view. Used for back button purposes. */

View File

@ -6,7 +6,6 @@ import { ViewItem } from './ViewItem';
export interface ViewStack { export interface ViewStack {
id: string; id: string;
routerOutlet: HTMLIonRouterOutletElement;
views: ViewItem[]; views: ViewItem[];
} }

View File

@ -30,6 +30,8 @@
"jsx-wrap-multiline": false, "jsx-wrap-multiline": false,
"forin": false, "forin": false,
"strict-type-predicates": false, "strict-type-predicates": false,
"no-unused-expression": false "no-unused-expression": false,
"no-constant-condition": false,
"no-empty-interface": false
} }
} }

View File

@ -75,7 +75,7 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
if (originalHref === currentHref) { if (originalHref === currentHref) {
this.context.navigate(originalHref, 'none'); this.context.navigate(originalHref, 'none');
} else { } else {
this.context.navigate(originalHref, 'back', 'replace'); this.context.navigate(originalHref, 'back', 'pop');
} }
} else { } else {
if (this.props.onIonTabsWillChange) { if (this.props.onIonTabsWillChange) {

View File

@ -5,7 +5,7 @@ export interface NavContextState {
getPageManager: () => any; getPageManager: () => any;
getStackManager: () => any; getStackManager: () => any;
goBack: (defaultHref?: string) => void; goBack: (defaultHref?: string) => void;
navigate: (path: string, direction?: RouterDirection | 'none', type?: 'push' | 'replace') => void; navigate: (path: string, direction?: RouterDirection | 'none', ionRouteAction?: 'push' | 'replace' | 'pop') => void;
hasIonicRouter: () => boolean; hasIonicRouter: () => boolean;
registerIonPage: (page: HTMLElement) => void; registerIonPage: (page: HTMLElement) => void;
currentPath: string | undefined; currentPath: string | undefined;