fix(v2): normalize location for route matching (#2393)

* fix(v2): normalize location path for route matching

* fix(v2): Memoize normalized pathnames

* fix(v2): fix wrong reference to this.state instead of normalized location

* Update normalizeLocation.js

Co-authored-by: Yangshun Tay <tay.yang.shun@gmail.com>
This commit is contained in:
Ramón Lamana 2020-03-23 18:34:50 +01:00 committed by GitHub
parent c05cb75cbc
commit a0570e48ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 6 deletions

View file

@ -11,6 +11,7 @@ import nprogress from 'nprogress';
import clientLifecyclesDispatcher from './client-lifecycles-dispatcher';
import preload from './preload';
import normalizeLocation from './normalizeLocation';
import 'nprogress/nprogress.css';
@ -37,19 +38,20 @@ class PendingNavigation extends React.Component {
// If `routeDidChange` is true, means the router is trying to navigate to a new
// route. We will preload the new route.
if (routeDidChange) {
const nextLocation = normalizeLocation(nextProps.location);
this.startProgressBar(delay);
// Save the location first.
this.previousLocation = this.props.location;
this.previousLocation = normalizeLocation(this.props.location);
this.setState({
nextRouteHasLoaded: false,
});
// Load data while the old screen remains.
preload(routes, nextProps.location.pathname)
preload(routes, nextLocation.pathname)
.then(() => {
clientLifecyclesDispatcher.onRouteUpdate({
previousLocation: this.previousLocation,
location: nextProps.location,
location: nextLocation,
});
// Route has loaded, we can reset previousLocation.
this.previousLocation = null;
@ -59,7 +61,7 @@ class PendingNavigation extends React.Component {
},
this.stopProgressBar,
);
const {hash} = nextProps.location;
const {hash} = nextLocation;
if (!hash) {
window.scrollTo(0, 0);
} else {
@ -94,7 +96,7 @@ class PendingNavigation extends React.Component {
this.clearProgressBarTimeout();
this.progressBarTimeout = setTimeout(() => {
clientLifecyclesDispatcher.onRouteUpdateDelayed({
location: this.props.location,
location: normalizeLocation(this.props.location),
});
nprogress.start();
}, delay);
@ -107,7 +109,9 @@ class PendingNavigation extends React.Component {
render() {
const {children, location} = this.props;
return <Route location={location} render={() => children} />;
return (
<Route location={normalizeLocation(location)} render={() => children} />
);
}
}

View file

@ -0,0 +1,70 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import normalizeLocation from '../normalizeLocation';
describe('normalizeLocation', () => {
test('rewrite locations with index.html', () => {
expect(
normalizeLocation({
pathname: '/docs/introduction/index.html',
search: '?search=foo',
hash: '#features',
}),
).toEqual({
pathname: '/docs/introduction',
search: '?search=foo',
hash: '#features',
});
expect(
normalizeLocation({
pathname: '/index.html',
search: '',
hash: '#features',
}),
).toEqual({
pathname: '/',
search: '',
hash: '#features',
});
});
test('untouched pathnames', () => {
expect(
normalizeLocation({
pathname: '/docs/introduction',
search: '',
hash: '#features',
}),
).toEqual({
pathname: '/docs/introduction',
search: '',
hash: '#features',
});
expect(
normalizeLocation({
pathname: '/docs/introduction/foo.html',
search: '',
hash: '#bar',
}),
).toEqual({
pathname: '/docs/introduction/foo.html',
search: '',
hash: '#bar',
});
expect(
normalizeLocation({
pathname: '/',
}),
).toEqual({
pathname: '/',
});
});
});

View file

@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// Memoize previously normalized pathnames.
const pathnames = {};
function normalizeLocation(location) {
if (pathnames[location.pathname]) {
return {
...location,
pathname: pathnames[location.pathname],
};
}
let pathname = location.pathname || '/';
pathname = pathname.trim().replace(/\/index\.html$/, '');
if (pathname === '') {
pathname = '/';
}
pathnames[location.pathname] = pathname;
return {
...location,
pathname,
};
}
export default normalizeLocation;