diff --git a/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap b/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap index 1018d06640..603882d5de 100644 --- a/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap +++ b/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap @@ -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", }, ], diff --git a/src/routers/StackRouter.js b/src/routers/StackRouter.js index 34d1250bf2..bc39e23d78 100644 --- a/src/routers/StackRouter.js +++ b/src/routers/StackRouter.js @@ -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, }; diff --git a/src/routers/__tests__/Routers-test.js b/src/routers/__tests__/Routers-test.js index 30b6fc8249..021a730bda 100644 --- a/src/routers/__tests__/Routers-test.js +++ b/src/routers/__tests__/Routers-test.js @@ -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( diff --git a/src/routers/__tests__/StackRouter-test.js b/src/routers/__tests__/StackRouter-test.js index d9f2272c5e..a38bb0d0d6 100644 --- a/src/routers/__tests__/StackRouter-test.js +++ b/src/routers/__tests__/StackRouter-test.js @@ -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 = () =>
; ChildNavigator.router = TabRouter({