mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 17:30:51 +08:00
Allow pure-wildcard route for nested navigators. (#2929)
* Allow pure-wildcard route for nested navigators. * Treat empty paths as they are.
This commit is contained in:
@@ -46,24 +46,42 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
const initialRouteName = stackConfig.initialRouteName || routeNames[0];
|
||||
|
||||
const initialChildRouter = childRouters[initialRouteName];
|
||||
const paths = stackConfig.paths || {};
|
||||
const pathsByRouteNames = { ...stackConfig.paths } || {};
|
||||
let paths = [];
|
||||
|
||||
// Build paths for each route
|
||||
routeNames.forEach(routeName => {
|
||||
let pathPattern = paths[routeName] || routeConfigs[routeName].path;
|
||||
const matchExact = !!pathPattern && !childRouters[routeName];
|
||||
if (typeof pathPattern !== 'string') {
|
||||
let pathPattern =
|
||||
pathsByRouteNames[routeName] || routeConfigs[routeName].path;
|
||||
let matchExact = !!pathPattern && !childRouters[routeName];
|
||||
if (pathPattern === undefined) {
|
||||
pathPattern = routeName;
|
||||
}
|
||||
const keys = [];
|
||||
let re = pathToRegexp(pathPattern, keys);
|
||||
let re, toPath, priority;
|
||||
if (typeof pathPattern === 'string') {
|
||||
// pathPattern may be either a string or a regexp object according to path-to-regexp docs.
|
||||
re = pathToRegexp(pathPattern, keys);
|
||||
toPath = pathToRegexp.compile(pathPattern);
|
||||
priority = 0;
|
||||
} else {
|
||||
// for wildcard match
|
||||
re = pathToRegexp('*', keys);
|
||||
toPath = () => '';
|
||||
matchExact = true;
|
||||
priority = -1;
|
||||
}
|
||||
if (!matchExact) {
|
||||
const wildcardRe = pathToRegexp(`${pathPattern}/*`, keys);
|
||||
re = new RegExp(`(?:${re.source})|(?:${wildcardRe.source})`);
|
||||
}
|
||||
paths[routeName] = { re, keys, toPath: pathToRegexp.compile(pathPattern) };
|
||||
pathsByRouteNames[routeName] = { re, keys, toPath, priority };
|
||||
});
|
||||
|
||||
paths = Object.entries(pathsByRouteNames);
|
||||
/* $FlowFixMe */
|
||||
paths.sort((a: [string, *], b: [string, *]) => b[1].priority - a[1].priority);
|
||||
|
||||
return {
|
||||
getComponentForState(state) {
|
||||
const activeChildRoute = state.routes[state.index];
|
||||
@@ -293,7 +311,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
const route = state.routes[state.index];
|
||||
const routeName = route.routeName;
|
||||
const screen = getScreenForRouteName(routeConfigs, routeName);
|
||||
const subPath = paths[routeName].toPath(route.params);
|
||||
const subPath = pathsByRouteNames[routeName].toPath(route.params);
|
||||
let path = subPath;
|
||||
let params = route.params;
|
||||
if (screen && screen.router) {
|
||||
@@ -328,7 +346,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
let pathMatchKeys;
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [routeName, path] of Object.entries(paths)) {
|
||||
for (const [routeName, path] of paths) {
|
||||
const { re, keys } = path;
|
||||
pathMatch = re.exec(pathNameToResolve);
|
||||
if (pathMatch && pathMatch.length) {
|
||||
@@ -340,6 +358,13 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
|
||||
// We didn't match -- return null
|
||||
if (!matchedRouteName) {
|
||||
// If the path is empty (null or empty string)
|
||||
// just return the initial route action
|
||||
if (!pathToResolve) {
|
||||
return NavigationActions.navigate({
|
||||
routeName: initialRouteName,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -353,6 +378,9 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
nestedAction = childRouters[matchedRouteName].getActionForPathAndParams(
|
||||
pathMatch.slice(pathMatchKeys.length).join('/') + nestedQueryString
|
||||
);
|
||||
if (!nestedAction) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// reduce the items of the query string. any query params may
|
||||
|
||||
@@ -54,6 +54,10 @@ const TestStackRouter = StackRouter({
|
||||
main: {
|
||||
screen: MainNavigator,
|
||||
},
|
||||
baz: {
|
||||
path: null,
|
||||
screen: FooNavigator,
|
||||
},
|
||||
auth: {
|
||||
screen: AuthNavigator,
|
||||
},
|
||||
@@ -277,6 +281,22 @@ describe('StackRouter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Correctly parses a path to the router connected to another router through a pure wildcard route into an action chain', () => {
|
||||
const uri = 'b/123';
|
||||
const action = TestStackRouter.getActionForPathAndParams(uri);
|
||||
expect(action).toEqual({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'baz',
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'bar',
|
||||
params: {
|
||||
barThing: '123',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Correctly returns null action for non-existent path', () => {
|
||||
const uri = 'asdf/1234';
|
||||
const action = TestStackRouter.getActionForPathAndParams(uri);
|
||||
@@ -944,6 +964,89 @@ describe('StackRouter', () => {
|
||||
expect(params.bazId).toEqual('321');
|
||||
});
|
||||
|
||||
test('Gets deep path with pure wildcard match', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
const ScreenC = () => <div />;
|
||||
ScreenA.router = StackRouter({
|
||||
Boo: { path: 'boo', screen: ScreenC },
|
||||
Baz: { path: 'baz/:bazId', screen: ScreenB },
|
||||
});
|
||||
ScreenC.router = StackRouter({
|
||||
Boo2: { path: '', screen: ScreenB },
|
||||
});
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
path: null,
|
||||
screen: ScreenA,
|
||||
},
|
||||
Bar: {
|
||||
screen: ScreenB,
|
||||
},
|
||||
});
|
||||
|
||||
{
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
params: {
|
||||
id: '123',
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Boo',
|
||||
routeName: 'Boo',
|
||||
routes: [{ key: 'Boo2', routeName: 'Boo2' }],
|
||||
},
|
||||
{ key: 'Baz', routeName: 'Baz', params: { bazId: '321' } },
|
||||
],
|
||||
},
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
const { path, params } = router.getPathAndParamsForState(state);
|
||||
expect(path).toEqual('baz/321');
|
||||
/* $FlowFixMe: params.id has to exist */
|
||||
expect(params.id).toEqual('123');
|
||||
/* $FlowFixMe: params.bazId has to exist */
|
||||
expect(params.bazId).toEqual('321');
|
||||
}
|
||||
|
||||
{
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
params: {
|
||||
id: '123',
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Boo',
|
||||
routeName: 'Boo',
|
||||
routes: [{ key: 'Boo2', routeName: 'Boo2' }],
|
||||
},
|
||||
{ key: 'Baz', routeName: 'Baz', params: { bazId: '321' } },
|
||||
],
|
||||
},
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
};
|
||||
const { path, params } = router.getPathAndParamsForState(state);
|
||||
expect(path).toEqual('boo/');
|
||||
expect(params).toEqual({ id: '123' });
|
||||
}
|
||||
});
|
||||
|
||||
test('Maps old actions (uses "Handles the reset action" test)', () => {
|
||||
global.console.warn = jest.fn();
|
||||
const router = StackRouter({
|
||||
|
||||
Reference in New Issue
Block a user