fix(react): support navigating to same page and route updates in IonRouterOutlet, fixes #19891, #19892, #19986

This commit is contained in:
Ely Lucas
2019-12-03 14:22:26 -07:00
committed by Ely Lucas
parent ab0f92e01f
commit 693ae21096
9 changed files with 180 additions and 90 deletions

View File

@ -2,22 +2,24 @@ import React from 'react';
import { generateId, isDevMode } from '../utils';
import { RouteManagerContext } from './RouteManagerContext';
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
import { View } from './View';
import { ViewItem } from './ViewItem';
import { ViewTransitionManager } from './ViewTransitionManager';
interface StackManagerProps {
id?: string;
routeManager: RouteManagerContextState;
children?: React.ReactNode;
}
interface StackManagerState {
routerOutletReady: boolean;
}
export class StackManager extends React.Component<StackManagerProps, StackManagerState> {
class StackManagerInner extends React.Component<StackManagerProps, StackManagerState> {
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
context!: React.ContextType<typeof RouteManagerContext>;
id: string;
constructor(props: StackManagerProps) {
@ -31,7 +33,7 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
}
componentDidMount() {
this.context.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
@ -39,33 +41,39 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
});
}
static getDerivedStateFromProps(props: StackManagerProps, state: StackManagerState) {
props.routeManager.syncRoute('', props.children);
return state;
}
componentWillUnmount() {
this.context.removeViewStack(this.id);
this.props.routeManager.removeViewStack(this.id);
}
handleViewSync(page: HTMLElement, viewId: string) {
this.context.syncView(page, viewId);
this.props.routeManager.syncView(page, viewId);
}
handleHideView(viewId: string) {
this.context.hideView(viewId);
this.props.routeManager.hideView(viewId);
}
renderChild(item: ViewItem) {
const component = React.cloneElement(item.route, {
renderChild(item: ViewItem, route: any) {
const component = React.cloneElement(route, {
computedMatch: item.routeData.match
});
return component;
}
render() {
const context = this.context;
const viewStack = context.viewStacks.get(this.id);
const routeManager = this.props.routeManager;
const viewStack = routeManager.viewStacks.get(this.id);
const views = (viewStack || { views: [] }).views.filter(x => x.show);
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
const { routerOutletReady } = this.state;
const childElements = routerOutletReady ? views.map(view => {
const route = routeManager.getRoute(view.routeId);
return (
<ViewTransitionManager
id={view.id}
@ -76,8 +84,9 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
onViewSync={this.handleViewSync}
onHideView={this.handleHideView}
view={view}
route={route}
>
{this.renderChild(view)}
{this.renderChild(view, route)}
</View>
</ViewTransitionManager>
);
@ -99,8 +108,14 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
return routerOutletChild;
}
}
static get contextType() {
return RouteManagerContext;
}
}
const withContext = (Component: any) => {
return (props: any) => (
<RouteManagerContext.Consumer>
{context => <Component {...props} routeManager={context} />}
</RouteManagerContext.Consumer>
)
}
export const StackManager = withContext(StackManagerInner);