diff --git a/packages/react-navigation/src/NavigationActions.js b/packages/react-navigation/src/NavigationActions.js index 18116f8e..e758e9bb 100644 --- a/packages/react-navigation/src/NavigationActions.js +++ b/packages/react-navigation/src/NavigationActions.js @@ -1,6 +1,7 @@ const BACK = 'Navigation/BACK'; const INIT = 'Navigation/INIT'; const NAVIGATE = 'Navigation/NAVIGATE'; +const POP = 'Navigation/POP'; const RESET = 'Navigation/RESET'; const SET_PARAMS = 'Navigation/SET_PARAMS'; const URI = 'Navigation/URI'; @@ -43,6 +44,12 @@ const navigate = createAction(NAVIGATE, payload => { return action; }); +const pop = createAction(POP, payload => ({ + type: POP, + n: payload && payload.n, + immediate: payload && payload.immediate, +})); + const reset = createAction(RESET, payload => ({ type: RESET, index: payload.index, @@ -123,6 +130,7 @@ export default { BACK, INIT, NAVIGATE, + POP, RESET, SET_PARAMS, URI, @@ -132,6 +140,7 @@ export default { back, init, navigate, + pop, reset, setParams, uri, diff --git a/packages/react-navigation/src/addNavigationHelpers.js b/packages/react-navigation/src/addNavigationHelpers.js index 36aea009..6faa1ab0 100644 --- a/packages/react-navigation/src/addNavigationHelpers.js +++ b/packages/react-navigation/src/addNavigationHelpers.js @@ -39,6 +39,10 @@ export default function(navigation) { ); return navigation.dispatch(NavigationActions.navigate(navigateTo)); }, + pop: (n, params) => + navigation.dispatch( + NavigationActions.pop({ n, immediate: params && params.immediate }) + ), /** * For updating current route params. For example the nav bar title and * buttons are based on the route params. diff --git a/packages/react-navigation/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap b/packages/react-navigation/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap index 9e240b17..007386e8 100644 --- a/packages/react-navigation/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap +++ b/packages/react-navigation/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap @@ -97,6 +97,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] = "dispatch": [Function], "goBack": [Function], "navigate": [Function], + "pop": [Function], "setParams": [Function], "state": Object { "index": 0, @@ -330,6 +331,7 @@ exports[`StackNavigator renders successfully 1`] = ` "dispatch": [Function], "goBack": [Function], "navigate": [Function], + "pop": [Function], "setParams": [Function], "state": Object { "index": 0, diff --git a/packages/react-navigation/src/routers/StackRouter.js b/packages/react-navigation/src/routers/StackRouter.js index 0201822e..a6371ca9 100644 --- a/packages/react-navigation/src/routers/StackRouter.js +++ b/packages/react-navigation/src/routers/StackRouter.js @@ -326,10 +326,17 @@ export default (routeConfigs, stackConfig = {}) => { }; } - if (action.type === NavigationActions.BACK) { - const key = action.key; + if ( + action.type === NavigationActions.BACK || + action.type === NavigationActions.POP + ) { + const { key, n, immediate } = action; let backRouteIndex = state.index; - if (key) { + if (action.type === NavigationActions.POP && n != null) { + // determine the index to go back *from*. In this case, n=1 means to go + // back from state.index, as if it were a normal "BACK" action + backRouteIndex = Math.max(1, state.index - n + 1); + } else if (key) { const backRoute = state.routes.find(route => route.key === key); backRouteIndex = state.routes.indexOf(backRoute); } @@ -338,7 +345,7 @@ export default (routeConfigs, stackConfig = {}) => { ...state, routes: state.routes.slice(0, backRouteIndex), index: backRouteIndex - 1, - isTransitioning: action.immediate !== true, + isTransitioning: immediate !== true, }; } } diff --git a/packages/react-navigation/src/routers/__tests__/TabRouter-test.js b/packages/react-navigation/src/routers/__tests__/TabRouter-test.js index 626336e4..92dcd74d 100644 --- a/packages/react-navigation/src/routers/__tests__/TabRouter-test.js +++ b/packages/react-navigation/src/routers/__tests__/TabRouter-test.js @@ -713,6 +713,47 @@ describe('TabRouter', () => { expect(state2).toEqual(state0); }); + test('pop action works as expected', () => { + const TestRouter = StackRouter({ + foo: { screen: () =>
}, + bar: { screen: () =>
}, + }); + + const state = { + index: 3, + isTransitioning: false, + routes: [ + { key: 'A', routeName: 'foo' }, + { key: 'B', routeName: 'bar', params: { bazId: '321' } }, + { key: 'C', routeName: 'foo' }, + { key: 'D', routeName: 'bar' }, + ], + }; + const poppedState = TestRouter.getStateForAction( + NavigationActions.pop(), + state + ); + expect(poppedState.routes.length).toBe(3); + expect(poppedState.index).toBe(2); + expect(poppedState.isTransitioning).toBe(true); + + const poppedState2 = TestRouter.getStateForAction( + NavigationActions.pop({ n: 2, immediate: true }), + state + ); + expect(poppedState2.routes.length).toBe(2); + expect(poppedState2.index).toBe(1); + expect(poppedState2.isTransitioning).toBe(false); + + const poppedState3 = TestRouter.getStateForAction( + NavigationActions.pop({ n: 5 }), + state + ); + expect(poppedState3.routes.length).toBe(1); + expect(poppedState3.index).toBe(0); + expect(poppedState3.isTransitioning).toBe(true); + }); + test('Inner actions are only unpacked if the current tab matches', () => { const PlainScreen = () =>
; const ScreenA = () =>
; diff --git a/packages/react-navigation/src/views/__tests__/__snapshots__/TabView-test.js.snap b/packages/react-navigation/src/views/__tests__/__snapshots__/TabView-test.js.snap index 4ea97b5f..c8a80c6f 100644 --- a/packages/react-navigation/src/views/__tests__/__snapshots__/TabView-test.js.snap +++ b/packages/react-navigation/src/views/__tests__/__snapshots__/TabView-test.js.snap @@ -224,6 +224,7 @@ exports[`TabBarBottom renders successfully 1`] = ` "dispatch": undefined, "goBack": [Function], "navigate": [Function], + "pop": [Function], "setParams": [Function], "state": Object { "key": "s1",