From 69f394be5b61523aab6f355a026acd0863e56928 Mon Sep 17 00:00:00 2001 From: Michael Lefkowitz <1746853+lfkwtz@users.noreply.github.com> Date: Wed, 11 Jul 2018 01:41:42 -0500 Subject: [PATCH] Feat/allow keyless replace (#4636) * allow key to be undefined on StackNavigation.replace method * added tests for replace action w/out key * fix typo * updated changelog * updated teests for clarity * added length check on routes to safely fallthrough to search --- CHANGELOG.md | 2 + src/routers/StackRouter.js | 10 +++- src/routers/__tests__/StackRouter-test.js | 71 +++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 514ad2a88e..0073694652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- StackNavigator.replace method no longer requires a key param. If the key is left undefined, the last screen in the stack will be replaced. ## [2.6.2] - [2018-07-06](https://github.com/react-navigation/react-navigation/releases/tag/2.6.2) ### Changed diff --git a/src/routers/StackRouter.js b/src/routers/StackRouter.js index 93fac47a7d..d47c6908b7 100644 --- a/src/routers/StackRouter.js +++ b/src/routers/StackRouter.js @@ -397,7 +397,15 @@ export default (routeConfigs, stackConfig = {}) => { // Handle replace action if (action.type === StackActions.REPLACE) { - const routeIndex = state.routes.findIndex(r => r.key === action.key); + let routeIndex; + + // If the key param is undefined, set the index to the last route in the stack + if (action.key === undefined && state.routes.length) { + routeIndex = state.routes.length - 1; + } else { + routeIndex = state.routes.findIndex(r => r.key === action.key); + } + // Only replace if the key matches one of our routes if (routeIndex !== -1) { const childRouter = childRouters[action.routeName]; diff --git a/src/routers/__tests__/StackRouter-test.js b/src/routers/__tests__/StackRouter-test.js index 03d1c053e7..8d5248b0cc 100644 --- a/src/routers/__tests__/StackRouter-test.js +++ b/src/routers/__tests__/StackRouter-test.js @@ -970,6 +970,77 @@ describe('StackRouter', () => { expect(replacedState2.routes[0].routeName).toEqual('bar'); }); + test('Replace action returns most recent route if no key is provided', () => { + const GrandChildNavigator = () =>
; + GrandChildNavigator.router = StackRouter({ + Quux: { screen: () => }, + Corge: { screen: () => }, + Grault: { screen: () => }, + }); + + const ChildNavigator = () => ; + ChildNavigator.router = StackRouter({ + Baz: { screen: () => }, + Woo: { screen: () => }, + Qux: { screen: GrandChildNavigator }, + }); + + const router = StackRouter({ + Foo: { screen: () => }, + Bar: { screen: ChildNavigator }, + }); + + const state = router.getStateForAction({ type: NavigationActions.INIT }); + const state2 = router.getStateForAction( + { + type: NavigationActions.NAVIGATE, + routeName: 'Bar', + }, + state + ); + const state3 = router.getStateForAction( + { + type: NavigationActions.NAVIGATE, + routeName: 'Qux', + }, + state2 + ); + const state4 = router.getStateForAction( + { + type: NavigationActions.NAVIGATE, + routeName: 'Corge', + }, + state3 + ); + const state5 = router.getStateForAction( + { + type: NavigationActions.NAVIGATE, + routeName: 'Grault', + }, + state4 + ); + + const replacedState = router.getStateForAction( + StackActions.replace({ + routeName: 'Woo', + params: { meaning: 42 }, + }), + state5 + ); + + const originalCurrentScreen = state5.routes[1].routes[1].routes[2]; + const replacedCurrentScreen = replacedState.routes[1].routes[1].routes[2]; + + expect(replacedState.routes[1].routes[1].index).toEqual(2); + expect(replacedState.routes[1].routes[1].routes.length).toEqual(3); + expect(replacedCurrentScreen.key).not.toEqual(originalCurrentScreen.key); + expect(replacedCurrentScreen.routeName).not.toEqual( + originalCurrentScreen.routeName + ); + expect(replacedCurrentScreen.routeName).toEqual('Woo'); + expect(replacedCurrentScreen.params.meaning).toEqual(42); + }); + test('Handles push transition logic with completion action', () => { const FooScreen = () => ; const BarScreen = () => ;