fix(react-router): remove page transition flicker on outlet mounting (#24667)

Resolves #24666
This commit is contained in:
Sean Perkins
2022-01-27 16:29:52 -05:00
committed by GitHub
parent 6d7b1444b6
commit bdb5c421d2
5 changed files with 83 additions and 4 deletions

View File

@@ -9,6 +9,14 @@ export class IonRouteInner extends React.PureComponent<IonRouteProps> {
path={this.props.path}
exact={this.props.exact}
render={this.props.render}
/**
* `computedMatch` is a private API in react-router v5 that
* has been removed in v6.
*
* This needs to be removed when we support v6.
*
* TODO: FW-647
*/
computedMatch={(this.props as any).computedMatch}
/>
);

View File

@@ -29,6 +29,8 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
isInOutlet: () => true,
};
private pendingPageTransition = false;
constructor(props: StackManagerProps) {
super(props);
this.registerIonPage = this.registerIonPage.bind(this);
@@ -46,8 +48,9 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
}
componentDidUpdate(prevProps: StackManagerProps) {
if (this.props.routeInfo.pathname !== prevProps.routeInfo.pathname) {
if (this.props.routeInfo.pathname !== prevProps.routeInfo.pathname || this.pendingPageTransition) {
this.handlePageTransition(this.props.routeInfo);
this.pendingPageTransition = false;
}
}
@@ -57,9 +60,15 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
}
async handlePageTransition(routeInfo: RouteInfo) {
// If routerOutlet isn't quite ready, give it another try in a moment
if (!this.routerOutletElement || !this.routerOutletElement.commit) {
setTimeout(() => this.handlePageTransition(routeInfo), 10);
/**
* The route outlet has not mounted yet. We need to wait for it to render
* before we can transition the page.
*
* Set a flag to indicate that we should transition the page after
* the component has updated.
*/
this.pendingPageTransition = true;
} else {
let enteringViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id);
let leavingViewItem = this.context.findLeavingViewItemByRouteInfo(routeInfo, this.id);

View File

@@ -99,7 +99,7 @@ export const IonTabs = /*@__PURE__*/ (() =>
return;
}
if (child.type === IonRouterOutlet || child.type.isRouterOutlet) {
outlet = React.cloneElement(child, { tabs: true });
outlet = React.cloneElement(child);
} else if (child.type === Fragment && child.props.children[0].type === IonRouterOutlet) {
outlet = child.props.children[0];
}

View File

@@ -24,6 +24,7 @@ import './theme/variables.css';
import Main from './pages/Main';
import OverlayHooks from './pages/overlay-hooks/OverlayHooks';
import OverlayComponents from './pages/overlay-components/OverlayComponents';
import Tabs from './pages/Tabs';
setupIonicReact();
@@ -33,6 +34,7 @@ const App: React.FC = () => (
<Route path="/" component={Main} />
<Route path="/overlay-hooks" component={OverlayHooks} />
<Route path="/overlay-components" component={OverlayComponents} />
<Route path="/tabs" component={Tabs} />
</IonReactRouter>
</IonApp>
);

View File

@@ -0,0 +1,60 @@
import { IonContent, IonHeader, IonLabel, IonPage, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, IonTitle, IonToolbar } from '@ionic/react';
import React from 'react';
import { Redirect, Route } from 'react-router';
const Tab1: React.FC = () => (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>
Tab 1
</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonLabel>Tab 1 page</IonLabel>
</IonContent>
</IonPage>
);
const Tab2: React.FC = () => (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>
Tab 2
</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonLabel>Tab 2 page</IonLabel>
</IonContent>
</IonPage>
);
const Tabs: React.FC = () => (
<IonTabs>
<IonRouterOutlet>
<Route exact path="/tabs/tab1">
<Tab1 />
</Route>
<Route exact path="/tabs/tab2">
<Tab2 />
</Route>
<Route exact path="/tabs">
<Redirect to ="/tabs/tab1" />
</Route>
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/tabs/tab1">
<IonLabel>Tab 1</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/tabs/tab2">
<IonLabel>Tab 2</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
)
export default Tabs;