fix(react): update paths of tab buttons when href changes in ion buttons, fixes #20321 (#20480)

This commit is contained in:
Ely Lucas
2020-02-13 15:05:40 -07:00
parent 82670fe4d0
commit 45d03baf98

View File

@ -12,27 +12,26 @@ type Props = LocalJSX.IonTabBar & {
slot?: 'bottom' | 'top'; slot?: 'bottom' | 'top';
}; };
interface Tab { interface TabUrls {
originalHref: string; originalHref: string;
currentHref: string; currentHref: string;
} }
interface State { interface State {
activeTab: string | undefined; activeTab: string | undefined;
tabs: { [key: string]: Tab }; tabs: { [key: string]: TabUrls };
} }
const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Props, State> { class IonTabBarUnwrapped extends React.PureComponent<Props, State> {
context!: React.ContextType<typeof NavContext>; context!: React.ContextType<typeof NavContext>;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
const tabs: { [key: string]: TabUrls; } = {};
const tabActiveUrls: { [key: string]: Tab } = {}; React.Children.forEach((props as any).children, (child: any) => {
React.Children.forEach(this.props.children, (child: any) => {
if (child != null && typeof child === 'object' && child.props && child.type === IonTabButton) { if (child != null && typeof child === 'object' && child.props && child.type === IonTabButton) {
tabActiveUrls[child.props.tab] = { tabs[child.props.tab] = {
originalHref: child.props.href, originalHref: child.props.href,
currentHref: child.props.href currentHref: child.props.href
}; };
@ -41,37 +40,54 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
this.state = { this.state = {
activeTab: undefined, activeTab: undefined,
tabs: tabActiveUrls tabs
}; };
this.onTabButtonClick = this.onTabButtonClick.bind(this);
this.renderTabButton = this.renderTabButton.bind(this);
} }
static getDerivedStateFromProps(props: Props, state: State) { static getDerivedStateFromProps(props: Props, state: State) {
const tabs = { ...state.tabs };
const activeTab = Object.keys(state.tabs) const activeTab = Object.keys(state.tabs)
.find(key => { .find(key => {
const href = state.tabs[key].originalHref; const href = state.tabs[key].originalHref;
return props.currentPath!.startsWith(href); return props.currentPath!.startsWith(href);
}); });
if (activeTab === undefined || (activeTab === state.activeTab && state.tabs[activeTab].currentHref === props.currentPath)) { // Check to see if the tab button href has changed, and if so, update it in the tabs state
return null; React.Children.forEach((props as any).children, (child: any) => {
if (child != null && typeof child === 'object' && child.props && child.type === IonTabButton) {
const tab = tabs[child.props.tab];
if (tab.originalHref !== child.props.href) {
tabs[child.props.tab] = {
originalHref: child.props.href,
currentHref: child.props.href
};
}
}
});
if (!(activeTab === undefined || (activeTab === state.activeTab && state.tabs[activeTab].currentHref === props.currentPath))) {
tabs[activeTab] = {
originalHref: tabs[activeTab].originalHref,
currentHref: props.currentPath!
};
} }
return { return {
activeTab, activeTab,
tabs: { tabs
...state.tabs,
[activeTab]: {
originalHref: state.tabs[activeTab].originalHref,
currentHref: props.currentPath
}
}
}; };
} }
private onTabButtonClick = (e: CustomEvent<{ href: string, selected: boolean, tab: string }>) => { private onTabButtonClick(e: CustomEvent<{ href: string, selected: boolean, tab: string; }>) {
const originalHref = this.state.tabs[e.detail.tab].originalHref; const originalHref = this.state.tabs[e.detail.tab].originalHref;
const currentHref = this.state.tabs[e.detail.tab].currentHref; const currentHref = e.detail.href;
if (this.state.activeTab === e.detail.tab) { const { activeTab: prevActiveTab } = this.state;
// Clicking the current tab will bring you back to the original href
if (prevActiveTab === e.detail.tab) {
if (originalHref === currentHref) { if (originalHref === currentHref) {
this.context.navigate(originalHref, 'none'); this.context.navigate(originalHref, 'none');
} else { } else {
@ -88,22 +104,27 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
} }
} }
private renderChild = (activeTab: string | null | undefined) => (child: (React.ReactElement<LocalJSX.IonTabButton & { onIonTabButtonClick: (e: CustomEvent) => void }>) | null | undefined) => { private renderTabButton(activeTab: string | null | undefined) {
if (child != null && child.props && child.type === IonTabButton) { return (child: (React.ReactElement<LocalJSX.IonTabButton & { onIonTabButtonClick: (e: CustomEvent) => void; }>) | null | undefined) => {
const href = (child.props.tab === activeTab) ? this.props.currentPath : (this.state.tabs[child.props.tab!].currentHref); if (child != null && child.props && child.type === IonTabButton) {
const href = (child.props.tab === activeTab) ? this.props.currentPath : (this.state.tabs[child.props.tab!].currentHref);
return React.cloneElement(child, { return React.cloneElement(child, {
href, href,
onIonTabButtonClick: this.onTabButtonClick onIonTabButtonClick: this.onTabButtonClick
}); });
} }
return null; return null;
};
} }
render() { render() {
const { activeTab } = this.state;
return ( return (
<IonTabBarInner {...this.props} selectedTab={this.state.activeTab}> <IonTabBarInner {...this.props} selectedTab={activeTab}>
{React.Children.map(this.props.children as any, this.renderChild(this.state.activeTab))} {React.Children.map(this.props.children as any, this.renderTabButton(activeTab))}
</IonTabBarInner> </IonTabBarInner>
); );
} }
@ -111,9 +132,9 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
static get contextType() { static get contextType() {
return NavContext; return NavContext;
} }
})(); }
export const IonTabBar: React.FC<Props> = props => { export const IonTabBar: React.FC<Props> = React.memo<Props>(props => {
const context = useContext(NavContext); const context = useContext(NavContext);
return ( return (
<IonTabBarUnwrapped <IonTabBarUnwrapped
@ -123,4 +144,4 @@ export const IonTabBar: React.FC<Props> = props => {
{props.children} {props.children}
</IonTabBarUnwrapped> </IonTabBarUnwrapped>
); );
}; });