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,38 +176,34 @@ 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> <IonTabBar slot="bottom">
<Redirect exact path="/" to="/tabs/schedule" /> <IonTabButton tab="schedule">
{/* <IonIcon name="calendar" />
Using the render method prop cuts down the number of renders your components will have due to route changes. <IonLabel>Schedule</IonLabel>
Use the component prop when your component depends on the RouterComponentProps passed in automatically. <IonBadge>6</IonBadge>
*/} </IonTabButton>
<Route path="/tabs/schedule" render={() => <SchedulePage />} exact={true} />
<Route path="/tabs/speakers" render={() => <SpeakerList />} exact={true} /> <IonTabButton tab="speakers">
<Route path="/tabs/map" render={() => <MapView />} exact={true} /> <IonIcon name="contacts" />
<Route path="/tabs/about" render={() => <About />} exact={true} /> <IonLabel>Speakers</IonLabel>
</IonRouterOutlet> </IonTabButton>
<IonTabBar slot="bottom">
<IonTabButton tab="schedule" href="/tabs/schedule"> <IonTabButton tab="map">
<IonIcon icon={calendar} /> <IonIcon name="map" />
<IonLabel>Schedule</IonLabel> <IonLabel>Map</IonLabel>
</IonTabButton> </IonTabButton>
<IonTabButton tab="speakers" href="/tabs/speakers">
<IonIcon icon={contacts} /> <IonTabButton tab="about">
<IonLabel>Speakers</IonLabel> <IonIcon name="information-circle" />
</IonTabButton> <IonLabel>About</IonLabel>
<IonTabButton tab="map" href="/tabs/map"> </IonTabButton>
<IonIcon icon={map} /> </IonTabBar>
<IonLabel>Map</IonLabel> </IonTabs>
</IonTabButton>
<IonTabButton tab="about" href="/tabs/about">
<IonIcon icon={informationCircle} />
<IonLabel>About</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
); );
``` ```

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';
} }
location.state = location.state || { direction };
this.setState({ this.setState({
location, location,
action action: ionRouteAction as IonRouteAction
}); });
this.currentRouteDirection = undefined;
this.currentIonRouteAction = undefined;
} }
setActiveView(location: HistoryLocation, action: HistoryAction) { setActiveView(location: HistoryLocation, action: IonRouteAction, viewStacks: ViewStacks) {
const viewStacks = Object.assign(new ViewStacks(), this.state.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' || action === 'replace') {
} else if (action === 'POP') {
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.isIonRoute = true;
view!.ionPageElement = page; this.ionPageElements[view.id] = page;
view!.isIonRoute = true; 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,43 +347,62 @@ 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 ((enteringEl === leavingEl) && direction && leavingViewHtml) { if (!('componentOnReady' in ionRouterOutlet)) {
// If a page is transitioning to another version of itself await waitUntilRouterOutletReady(ionRouterOutlet);
// we clone it so we can have an animation to show }
const newLeavingElement = clonePageElement(leavingViewHtml);
ionRouterOutlet.appendChild(newLeavingElement); if ((enteringEl === leavingEl) && direction && leavingViewHtml) {
await ionRouterOutlet.commit(enteringEl, newLeavingElement, { // If a page is transitioning to another version of itself
deepWait: true, // we clone it so we can have an animation to show
duration: direction === undefined ? 0 : undefined, const newLeavingElement = clonePageElement(leavingViewHtml);
direction, ionRouterOutlet.appendChild(newLeavingElement);
showGoBack, await ionRouterOutlet.commit(enteringEl, newLeavingElement, {
progressAnimation: false deepWait: true,
}); duration: direction === undefined ? 0 : undefined,
ionRouterOutlet.removeChild(newLeavingElement); direction,
showGoBack,
progressAnimation: false
});
ionRouterOutlet.removeChild(newLeavingElement);
} else {
await ionRouterOutlet.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined ? 0 : undefined,
direction,
showGoBack,
progressAnimation: false
});
}
if (leavingEl && (enteringEl !== leavingEl)) {
/** add hidden attributes */
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
}
} else { } else {
await ionRouterOutlet.commit(enteringEl, leavingEl, { enteringEl.classList.remove('ion-page-invisible');
deepWait: true, enteringEl.style.zIndex = '101';
duration: direction === undefined ? 0 : undefined, this.firstRender = false;
direction,
showGoBack,
progressAnimation: false
});
}
if (leavingEl && (enteringEl !== leavingEl)) {
/** add hidden attributes */
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
} }
} }
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) {
this.props.history.push(path); case 'push':
} else { this.currentRouteDirection = direction;
this.props.history.replace(path); this.props.history.push(path);
break;
case 'pop':
this.currentRouteDirection = direction || 'back';
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;