Consistent treatment of route keys (#3474)

This problem was found and fixed by @matthargett and @jayphelps in #3397. I’m just rebasing and cleaning a few things up
This commit is contained in:
Eric Vicenti
2018-02-08 15:28:27 -08:00
parent 261afab172
commit 48c82e4ec3
4 changed files with 168 additions and 44 deletions

View File

@@ -108,7 +108,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
"key": "StackRouterRoot",
"routes": Array [
Object {
"key": "Init-id-0-1",
"key": "id-0-1",
"routeName": "Home",
},
],
@@ -347,7 +347,7 @@ exports[`StackNavigator renders successfully 1`] = `
"key": "StackRouterRoot",
"routes": Array [
Object {
"key": "Init-id-0-0",
"key": "id-0-0",
"routeName": "Home",
},
],

View File

@@ -101,23 +101,32 @@ export default (routeConfigs, stackConfig = {}) => {
// Set up the initial state if needed
if (!state) {
let route = {};
if (
behavesLikePushAction(action) &&
childRouters[action.routeName] !== undefined
) {
const childRouter = childRouters[action.routeName];
// This is a push-like action, and childRouter will be a router or null if we are responsible for this routeName
if (behavesLikePushAction(action) && childRouter !== undefined) {
let childState = {};
// The router is null for normal leaf routes
if (childRouter !== null) {
const childAction =
action.action ||
NavigationActions.init({ params: action.params });
childState = childRouter.getStateForAction(childAction);
}
return {
key: 'StackRouterRoot',
isTransitioning: false,
index: 0,
routes: [
{
routeName: action.routeName,
params: action.params,
key: `Init-${generateKey()}`,
...childState,
key: action.key || generateKey(),
routeName: action.routeName,
},
],
};
}
if (initialChildRouter) {
route = initialChildRouter.getStateForAction(
NavigationActions.navigate({
@@ -135,11 +144,10 @@ export default (routeConfigs, stackConfig = {}) => {
};
route = {
...route,
routeName: initialRouteName,
key: `Init-${generateKey()}`,
...(params ? { params } : {}),
routeName: initialRouteName,
key: action.key || generateKey(),
};
// eslint-disable-next-line no-param-reassign
state = {
key: 'StackRouterRoot',
isTransitioning: false,
@@ -206,8 +214,8 @@ export default (routeConfigs, stackConfig = {}) => {
params: action.params,
// merge the child state in this order to allow params override
...childState,
key: action.newKey || generateKey(),
routeName: action.routeName,
key: action.newKey || generateKey(),
};
return { ...state, routes };
}
@@ -259,7 +267,7 @@ export default (routeConfigs, stackConfig = {}) => {
};
}
}
const key = action.key || generateKey();
if (childRouter) {
const childAction =
action.action || NavigationActions.init({ params: action.params });
@@ -267,14 +275,14 @@ export default (routeConfigs, stackConfig = {}) => {
params: action.params,
// merge the child state in this order to allow params override
...childRouter.getStateForAction(childAction),
key,
routeName: action.routeName,
key: action.key || generateKey(),
};
} else {
route = {
params: action.params,
key,
routeName: action.routeName,
key: action.key || generateKey(),
};
}
return {
@@ -326,11 +334,12 @@ export default (routeConfigs, stackConfig = {}) => {
routeToPush = navigatedChildRoute;
}
if (routeToPush) {
return StateUtils.push(state, {
const route = {
...routeToPush,
key: generateKey(),
routeName: childRouterName,
});
key: action.key || generateKey(),
};
return StateUtils.push(state, route);
}
}
}
@@ -369,20 +378,16 @@ export default (routeConfigs, stackConfig = {}) => {
...state,
routes: resetAction.actions.map(childAction => {
const router = childRouters[childAction.routeName];
let childState = {};
if (router) {
return {
...childAction,
...router.getStateForAction(childAction),
routeName: childAction.routeName,
key: generateKey(),
};
childState = router.getStateForAction(childAction);
}
const route = {
...childAction,
key: generateKey(),
return {
params: childAction.params,
...childState,
routeName: childAction.routeName,
key: childAction.key || generateKey(),
};
delete route.type;
return route;
}),
index: action.index,
};

View File

@@ -7,7 +7,6 @@ import TabRouter from '../TabRouter';
import NavigationActions from '../../NavigationActions';
import addNavigationHelpers from '../../addNavigationHelpers';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
beforeEach(() => {
@@ -19,7 +18,7 @@ const ROUTERS = {
StackRouter,
};
const dummyEventSubscriber = (name: string, handler: (*) => void) => ({
const dummyEventSubscriber = (name, handler) => ({
remove: () => {},
});
@@ -111,8 +110,8 @@ test('Handles no-op actions with tabs within stack router', () => {
type: NavigationActions.NAVIGATE,
routeName: 'Qux',
});
expect(state1.routes[0].key).toEqual('Init-id-0');
expect(state2.routes[0].key).toEqual('Init-id-1');
expect(state1.routes[0].key).toEqual('id-0');
expect(state2.routes[0].key).toEqual('id-1');
state1.routes[0].key = state2.routes[0].key;
expect(state1).toEqual(state2);
const state3 = TestRouter.getStateForAction(
@@ -140,7 +139,7 @@ test('Handles deep action', () => {
key: 'StackRouterRoot',
routes: [
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Bar',
},
],
@@ -180,8 +179,8 @@ test('Supports lazily-evaluated getScreen', () => {
immediate: true,
routeName: 'Qux',
});
expect(state1.routes[0].key).toEqual('Init-id-0');
expect(state2.routes[0].key).toEqual('Init-id-1');
expect(state1.routes[0].key).toEqual('id-0');
expect(state2.routes[0].key).toEqual('id-1');
state1.routes[0].key = state2.routes[0].key;
expect(state1).toEqual(state2);
const state3 = TestRouter.getStateForAction(

View File

@@ -355,7 +355,7 @@ describe('StackRouter', () => {
index: 0,
isTransitioning: false,
key: 'StackRouterRoot',
routes: [{ key: 'Init-id-0', routeName: 'foo' }],
routes: [{ key: 'id-0', routeName: 'foo' }],
});
const pushedState = TestRouter.getStateForAction(
NavigationActions.navigate({ routeName: 'qux' }),
@@ -571,7 +571,7 @@ describe('StackRouter', () => {
key: 'StackRouterRoot',
routes: [
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Foo',
},
],
@@ -599,7 +599,7 @@ describe('StackRouter', () => {
key: 'StackRouterRoot',
routes: [
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Foo',
},
],
@@ -696,7 +696,7 @@ describe('StackRouter', () => {
key: 'StackRouterRoot',
routes: [
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Foo',
},
],
@@ -724,7 +724,7 @@ describe('StackRouter', () => {
key: 'StackRouterRoot',
routes: [
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Foo',
},
],
@@ -798,7 +798,7 @@ describe('StackRouter', () => {
key: 'StackRouterRoot',
routes: [
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Bar',
},
],
@@ -907,14 +907,14 @@ describe('StackRouter', () => {
{
type: NavigationActions.SET_PARAMS,
params: { name: 'foobar' },
key: 'Init-id-0',
key: 'id-0',
},
state
);
expect(state2 && state2.index).toEqual(0);
expect(state2 && state2.routes[0].routes[0].routes).toEqual([
{
key: 'Init-id-0',
key: 'id-0',
routeName: 'Quux',
params: { name: 'foobar' },
},
@@ -1133,6 +1133,126 @@ describe('StackRouter', () => {
]);
});
test('Handles the navigate action with params and nested StackRouter as a first action', () => {
const state = TestStackRouter.getStateForAction({
type: NavigationActions.NAVIGATE,
routeName: 'main',
params: {
code: 'test',
foo: 'bar',
},
action: {
type: NavigationActions.NAVIGATE,
routeName: 'profile',
params: {
id: '4',
code: 'test',
foo: 'bar',
},
action: {
type: NavigationActions.NAVIGATE,
routeName: 'list',
params: {
id: '10259959195',
code: 'test',
foo: 'bar',
},
},
},
});
expect(state).toEqual({
index: 0,
isTransitioning: false,
key: 'StackRouterRoot',
routes: [
{
index: 0,
isTransitioning: false,
key: 'id-2',
params: { code: 'test', foo: 'bar' },
routeName: 'main',
routes: [
{
index: 0,
isTransitioning: false,
key: 'id-1',
params: { code: 'test', foo: 'bar', id: '4' },
routeName: 'profile',
routes: [
{
key: 'id-0',
params: { code: 'test', foo: 'bar', id: '10259959195' },
routeName: 'list',
type: undefined,
},
],
},
],
},
],
});
const state2 = TestStackRouter.getStateForAction({
type: NavigationActions.NAVIGATE,
routeName: 'main',
params: {
code: '',
foo: 'bar',
},
action: {
type: NavigationActions.NAVIGATE,
routeName: 'profile',
params: {
id: '4',
code: '',
foo: 'bar',
},
action: {
type: NavigationActions.NAVIGATE,
routeName: 'list',
params: {
id: '10259959195',
code: '',
foo: 'bar',
},
},
},
});
expect(state2).toEqual({
index: 0,
isTransitioning: false,
key: 'StackRouterRoot',
routes: [
{
index: 0,
isTransitioning: false,
key: 'id-5',
params: { code: '', foo: 'bar' },
routeName: 'main',
routes: [
{
index: 0,
isTransitioning: false,
key: 'id-4',
params: { code: '', foo: 'bar', id: '4' },
routeName: 'profile',
routes: [
{
key: 'id-3',
params: { code: '', foo: 'bar', id: '10259959195' },
routeName: 'list',
type: undefined,
},
],
},
],
},
],
});
});
test('Handles the navigate action with params and nested TabRouter', () => {
const ChildNavigator = () => <div />;
ChildNavigator.router = TabRouter({