mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 01:03:03 +08:00
fix(react): improve view matching logic (#22569)
This commit is contained in:
@ -118,15 +118,14 @@ class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
|
||||
};
|
||||
}
|
||||
if (action === 'POP') {
|
||||
const ri = this.locationHistory.current();
|
||||
if (ri && ri.pushedByRoute) {
|
||||
const prevInfo = this.locationHistory.findLastLocation(ri);
|
||||
const currentRoute = this.locationHistory.current();
|
||||
if (currentRoute && currentRoute.pushedByRoute) {
|
||||
const prevInfo = this.locationHistory.findLastLocation(currentRoute);
|
||||
this.incomingRouteParams = { ...prevInfo, routeAction: 'pop', routeDirection: 'back' };
|
||||
} else {
|
||||
const direction = 'none';
|
||||
this.incomingRouteParams = {
|
||||
routeAction: 'pop',
|
||||
routeDirection: direction,
|
||||
routeDirection: 'none',
|
||||
tab: this.currentTab,
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ cd ../../react
|
||||
npm run build
|
||||
npm pack
|
||||
cd ../react-router/test-app
|
||||
npm i ../../react/ionic-react-5.3.0.tgz
|
||||
npm i ../../react/ionic-react-5.5.0.tgz
|
||||
npm run start
|
||||
|
||||
|
||||
|
12
packages/react-router/test-app/package-lock.json
generated
12
packages/react-router/test-app/package-lock.json
generated
@ -1513,19 +1513,19 @@
|
||||
}
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.3.2.tgz",
|
||||
"integrity": "sha512-s/SDnS993fnZ3d6EzOlURHBbc2aI2/WsZSsCLgnJz3G+KUO4hY/2RQvdmtl0FZpDHMSyehG6tRgFFXvnFSI9CQ==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.5.0.tgz",
|
||||
"integrity": "sha512-LHKRQ4kmJN6Z9aqbYajYIQi3CFULjO/UI16frMu2Oc4rx8uewMNPBClAR5Dhy/jSkL3MiIOiuQzLPO4Kyd/zzw==",
|
||||
"requires": {
|
||||
"ionicons": "^5.1.2",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@ionic/react": {
|
||||
"version": "file:../../react/ionic-react-5.3.2.tgz",
|
||||
"integrity": "sha512-ojxb7wNfikrpREjlZwRhwzLEmHtdNGqQIX73pyV/kZbgKx4z2ay5knSbdguYeIjPMQHUjCCgyog77hONxGf5Rw==",
|
||||
"version": "file:../../react/ionic-react-5.5.0.tgz",
|
||||
"integrity": "sha512-/MiNrJbGH9hGPglF5dGxNKSA+XmYFuzOO45DEZ99lOUS0fIXHMfcQ4PSiWEA/n3++qQIgLTHPn3IG4zDaYYpTQ==",
|
||||
"requires": {
|
||||
"@ionic/core": "5.3.2",
|
||||
"@ionic/core": "5.5.0",
|
||||
"ionicons": "^5.1.2",
|
||||
"tslib": "*"
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
"@capacitor/android": "^2.2.0",
|
||||
"@capacitor/core": "1.5.2",
|
||||
"@capacitor/ios": "^2.2.0",
|
||||
"@ionic/react": "file:../../react/ionic-react-5.3.2.tgz",
|
||||
"@ionic/react": "file:../../react/ionic-react-5.5.0.tgz",
|
||||
"@svgr/webpack": "4.3.3",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.4.0",
|
||||
|
@ -55,7 +55,7 @@ export class LocationHistory {
|
||||
const routeInfos = this._getRouteInfosByKey(routeInfo.tab);
|
||||
if (routeInfos) {
|
||||
// If the latest routeInfo is the same (going back and forth between tabs), replace it
|
||||
if (routeInfos[routeInfos.length - 1]?.id === routeInfo.id) {
|
||||
if (this._areRoutesEqual(routeInfos[routeInfos.length - 1], routeInfo)) {
|
||||
routeInfos.pop();
|
||||
}
|
||||
routeInfos.push(routeInfo);
|
||||
@ -63,27 +63,27 @@ export class LocationHistory {
|
||||
this.locationHistory.push(routeInfo);
|
||||
}
|
||||
|
||||
private _areRoutesEqual(route1?: RouteInfo, route2?: RouteInfo) {
|
||||
if(!route1 || !route2) {
|
||||
return false;
|
||||
}
|
||||
return route1.pathname === route2.pathname && route1.search === route2.search;
|
||||
}
|
||||
|
||||
private _pop(routeInfo: RouteInfo) {
|
||||
const routeInfos = this._getRouteInfosByKey(routeInfo.tab);
|
||||
let ri: RouteInfo;
|
||||
|
||||
if (routeInfos) {
|
||||
// Pop all routes until we are back
|
||||
ri = routeInfos[routeInfos.length - 1];
|
||||
while (ri && ri.id !== routeInfo.id) {
|
||||
routeInfos.pop();
|
||||
ri = routeInfos[routeInfos.length - 1];
|
||||
}
|
||||
// Replace with updated route
|
||||
// Pop the previous route
|
||||
routeInfos.pop();
|
||||
// Replace the current route with an updated version
|
||||
routeInfos.pop();
|
||||
routeInfos.push(routeInfo);
|
||||
}
|
||||
|
||||
ri = this.locationHistory[this.locationHistory.length - 1];
|
||||
while (ri && ri.id !== routeInfo.id) {
|
||||
this.locationHistory.pop();
|
||||
ri = this.locationHistory[this.locationHistory.length - 1];
|
||||
}
|
||||
// Replace with updated route
|
||||
// Pop the previous route
|
||||
this.locationHistory.pop();
|
||||
// Replace the current route with an updated version
|
||||
this.locationHistory.pop();
|
||||
this.locationHistory.push(routeInfo);
|
||||
}
|
||||
|
219
packages/react/src/routing/__tests__/LocationHistory.spec.ts
Normal file
219
packages/react/src/routing/__tests__/LocationHistory.spec.ts
Normal file
@ -0,0 +1,219 @@
|
||||
import { LocationHistory } from '../LocationHistory';
|
||||
import { RouteInfo } from '../../models/RouteInfo';
|
||||
import { generateId } from '../../utils/generateId';
|
||||
|
||||
let log = false;
|
||||
|
||||
describe('LocationHistory', () => {
|
||||
let locationHistory: LocationHistory;
|
||||
logHistory();
|
||||
log = true;
|
||||
|
||||
beforeEach(() => {
|
||||
locationHistory = new LocationHistory();
|
||||
});
|
||||
|
||||
describe('Popping history', () => {
|
||||
describe('page1 > page2', () => {
|
||||
beforeEach(() => {
|
||||
const firstRoute = createRoute('/page1');
|
||||
const secondRoute = createRoute('/page2', firstRoute);
|
||||
locationHistory.add(firstRoute);
|
||||
locationHistory.add(secondRoute);
|
||||
});
|
||||
|
||||
it('then should be at /page2 canGoBack should be true', () => {
|
||||
expect(currentRoute().pathname).toEqual('/page2');
|
||||
expect(locationHistory.canGoBack()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('when going back, should be on /page1 and canGoBack should be false', () => {
|
||||
popRoute();
|
||||
expect(currentRoute().pathname).toEqual('/page1');
|
||||
expect(locationHistory.canGoBack()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tab1 > tab2 > Back', () => {
|
||||
beforeEach(() => {
|
||||
const tab1Route = createRoute('/tab1', undefined, 'tab1');
|
||||
const tab2Route = createRoute('/tab2', tab1Route, 'tab2');
|
||||
locationHistory.add(tab1Route);
|
||||
locationHistory.add(tab2Route);
|
||||
popRoute();
|
||||
});
|
||||
|
||||
it('then should be on tab1 and should not be able to go back', () => {
|
||||
expect(currentRoute().pathname).toEqual('/tab1');
|
||||
expect(locationHistory.canGoBack()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tab1 > tab2 > tab3', () => {
|
||||
beforeEach(() => {
|
||||
const tab1Route = createRoute('/tab1', undefined, 'tab1');
|
||||
const tab2Route = createRoute('/tab2', tab1Route, 'tab2');
|
||||
const tab3Route = createRoute('/tab3', tab2Route, 'tab3');
|
||||
locationHistory.add(tab1Route);
|
||||
locationHistory.add(tab2Route);
|
||||
locationHistory.add(tab3Route);
|
||||
});
|
||||
|
||||
it('when back once, then should be on tab2 and should be able to go back', () => {
|
||||
popRoute();
|
||||
expect(currentRoute().pathname).toEqual('/tab2');
|
||||
expect(locationHistory.canGoBack).toBeTruthy();
|
||||
});
|
||||
|
||||
it('when going back twice, should be on tab1 and not be able to go back', () => {
|
||||
popRoute();
|
||||
popRoute();
|
||||
expect(currentRoute().pathname).toEqual('/tab1');
|
||||
expect(locationHistory.canGoBack()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tab1 > tab1/details/1, then tab1/details/2', () => {
|
||||
beforeEach(() => {
|
||||
const homeRoute = createRoute('/tab1', undefined, 'tab1');
|
||||
const details1Route = createRoute('/tab1/details/1', undefined, 'tab1');
|
||||
const details2Route = createRoute('/tab1/details/2', undefined, 'tab1');
|
||||
locationHistory.add(homeRoute);
|
||||
locationHistory.add(details1Route);
|
||||
locationHistory.add(details2Route);
|
||||
});
|
||||
|
||||
it('then should be at details2 and able to go back', () => {
|
||||
expect(currentRoute().pathname).toEqual('/tab1/details/2');
|
||||
expect(locationHistory.canGoBack()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('when going back, should be at details1 and able to go back', () => {
|
||||
popRoute();
|
||||
expect(currentRoute().pathname).toEqual('/tab1/details/1');
|
||||
expect(locationHistory.canGoBack()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('when going back twice, should be at home and not able to go back', () => {
|
||||
popRoute();
|
||||
popRoute();
|
||||
expect(currentRoute().pathname).toEqual('/tab1');
|
||||
expect(locationHistory.canGoBack()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Switching tabs', () => {
|
||||
describe('tab1 > tab2 > tab1', () => {
|
||||
beforeEach(() => {
|
||||
const tab1Route = createRoute('/tab1', undefined, 'tab1');
|
||||
const tab2Route = createRoute('/tab2', tab1Route, 'tab2');
|
||||
const tab1_2Route = createRoute('/tab1', tab2Route, 'tab1');
|
||||
locationHistory.add(tab1Route);
|
||||
locationHistory.add(tab2Route);
|
||||
locationHistory.add(tab1_2Route);
|
||||
});
|
||||
|
||||
it('then locationHistory should have 3 entries', () =>
|
||||
expect((locationHistory as any).locationHistory.length).toEqual(3));
|
||||
|
||||
it('then tab1 and tab2 should have one entry', () => {
|
||||
expect((locationHistory as any).tabHistory['tab1'].length).toEqual(1);
|
||||
expect((locationHistory as any).tabHistory['tab2'].length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tab1 > tab2 > tab3 > Back', () => {
|
||||
beforeEach(() => {
|
||||
const tab1Route = createRoute('/tab1', undefined, 'tab1');
|
||||
const tab2Route = createRoute('/tab2', tab1Route, 'tab2');
|
||||
const tab3Route = createRoute('/tab3', tab2Route, 'tab3');
|
||||
locationHistory.add(tab1Route);
|
||||
locationHistory.add(tab2Route);
|
||||
locationHistory.add(tab3Route);
|
||||
popRoute();
|
||||
});
|
||||
|
||||
it('when going back, then locationHistory should have 2 entries', () => {
|
||||
expect((locationHistory as any).locationHistory.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('when going back, then tab1, tab2, and tab3 should have one entry', () => {
|
||||
expect((locationHistory as any).tabHistory['tab1'].length).toEqual(1);
|
||||
expect((locationHistory as any).tabHistory['tab2'].length).toEqual(1);
|
||||
expect((locationHistory as any).tabHistory['tab3'].length).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Replacing history', () => {
|
||||
describe('tab1 > tab1/details/1, tab1/details/2, Replace to tab1/details/3', () => {
|
||||
beforeEach(() => {
|
||||
const homeRoute = createRoute('/tab1', undefined, 'tab1');
|
||||
const details1Route = createRoute('/tab1/details/1', homeRoute, 'tab1');
|
||||
const details2Route = createRoute('/tab1/details/2', details1Route, 'tab1');
|
||||
const details3Route = createRoute('/tab1/details/3', details2Route, 'tab1', 'replace');
|
||||
locationHistory.add(homeRoute);
|
||||
locationHistory.add(details1Route);
|
||||
locationHistory.add(details2Route);
|
||||
locationHistory.add(details3Route);
|
||||
});
|
||||
|
||||
it('then should be at details3', () => {
|
||||
expect(currentRoute().pathname).toEqual('/tab1/details/3');
|
||||
});
|
||||
|
||||
it('tab1 history should have 3 entries and they should have correct paths', () => {
|
||||
expect((locationHistory as any).tabHistory['tab1'].length).toEqual(3);
|
||||
expect((locationHistory as any).tabHistory['tab1'][0].pathname).toEqual('/tab1');
|
||||
expect((locationHistory as any).tabHistory['tab1'][1].pathname).toEqual('/tab1/details/1');
|
||||
expect((locationHistory as any).tabHistory['tab1'][2].pathname).toEqual('/tab1/details/3');
|
||||
});
|
||||
|
||||
it('locationHistory should have 3 entries and they should have correct paths', () => {
|
||||
expect((locationHistory as any).locationHistory.length).toEqual(3);
|
||||
expect((locationHistory as any).locationHistory[0].pathname).toEqual('/tab1');
|
||||
expect((locationHistory as any).locationHistory[1].pathname).toEqual('/tab1/details/1');
|
||||
expect((locationHistory as any).locationHistory[2].pathname).toEqual('/tab1/details/3');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function currentRoute() {
|
||||
return locationHistory.current();
|
||||
}
|
||||
|
||||
function popRoute() {
|
||||
const previous = locationHistory.previous();
|
||||
previous.routeDirection = 'back';
|
||||
previous.routeAction = 'pop';
|
||||
locationHistory.add(previous);
|
||||
}
|
||||
|
||||
function logHistory() {
|
||||
if (log) {
|
||||
console.log('locationHistory', (locationHistory as any).locationHistory);
|
||||
console.log('tabHistory', (locationHistory as any).tabHistory);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function createRoute(
|
||||
pathname: string = '',
|
||||
prevRoute?: RouteInfo,
|
||||
tab?: string,
|
||||
routeAction = 'push'
|
||||
) {
|
||||
const routeInfo: RouteInfo = {
|
||||
id: generateId(),
|
||||
lastPathname: prevRoute?.pathname,
|
||||
prevRouteLastPathname: prevRoute?.lastPathname,
|
||||
routeAction: routeAction as any,
|
||||
routeDirection: 'forward',
|
||||
pushedByRoute: prevRoute?.pathname,
|
||||
pathname: pathname,
|
||||
tab: tab,
|
||||
search: '',
|
||||
};
|
||||
return routeInfo;
|
||||
}
|
@ -24,7 +24,6 @@
|
||||
"target": "es2017"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["**/__tests__/**"],
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false
|
||||
}
|
||||
|
Reference in New Issue
Block a user