feat: add a JUMP_TO action for switch

Add a JUMP_TO action in switch navigator to handle navigation actions explictly instead of the magical NAVIGATE action.
This commit is contained in:
satyajit.happy
2019-04-20 13:49:49 +02:00
parent b13b5805a5
commit 6101d7c181
14 changed files with 104 additions and 86 deletions

View File

@@ -1,15 +1,17 @@
const BACK = 'Navigation/BACK';
const INIT = 'Navigation/INIT';
const NAVIGATE = 'Navigation/NAVIGATE';
const SET_PARAMS = 'Navigation/SET_PARAMS';
// Action constants
export const BACK = 'Navigation/BACK';
export const INIT = 'Navigation/INIT';
export const NAVIGATE = 'Navigation/NAVIGATE';
export const SET_PARAMS = 'Navigation/SET_PARAMS';
const back = (payload = {}) => ({
// Action creators
export const back = (payload = {}) => ({
type: BACK,
key: payload.key,
immediate: payload.immediate,
});
const init = (payload = {}) => {
export const init = (payload = {}) => {
const action = {
type: INIT,
};
@@ -19,7 +21,7 @@ const init = (payload = {}) => {
return action;
};
const navigate = payload => {
export const navigate = payload => {
const action = {
type: NAVIGATE,
routeName: payload.routeName,
@@ -36,22 +38,9 @@ const navigate = payload => {
return action;
};
const setParams = payload => ({
export const setParams = payload => ({
type: SET_PARAMS,
key: payload.key,
params: payload.params,
preserveFocus: true,
});
export default {
// Action constants
BACK,
INIT,
NAVIGATE,
SET_PARAMS,
// Action creators
back,
init,
navigate,
setParams,
};

View File

@@ -1,4 +1,4 @@
import NavigationActions from '../NavigationActions';
import * as NavigationActions from '../NavigationActions';
describe('generic navigation actions', () => {
const params = { foo: 'bar' };
@@ -51,6 +51,7 @@ describe('generic navigation actions', () => {
).toEqual({
type: NavigationActions.SET_PARAMS,
key: 'test',
preserveFocus: true,
params,
});
});

View File

@@ -28,10 +28,13 @@ module.exports = {
// Actions
get NavigationActions() {
return require('./NavigationActions').default;
return require('./NavigationActions');
},
get StackActions() {
return require('./routers/StackActions').default;
return require('./routers/StackActions');
},
get SwitchActions() {
return require('./routers/SwitchActions');
},
// Routers

View File

@@ -1,53 +1,38 @@
const POP = 'Navigation/POP';
const POP_TO_TOP = 'Navigation/POP_TO_TOP';
const PUSH = 'Navigation/PUSH';
const RESET = 'Navigation/RESET';
const REPLACE = 'Navigation/REPLACE';
const COMPLETE_TRANSITION = 'Navigation/COMPLETE_TRANSITION';
export const POP = 'Navigation/POP';
export const POP_TO_TOP = 'Navigation/POP_TO_TOP';
export const PUSH = 'Navigation/PUSH';
export const RESET = 'Navigation/RESET';
export const REPLACE = 'Navigation/REPLACE';
export const COMPLETE_TRANSITION = 'Navigation/COMPLETE_TRANSITION';
const pop = payload => ({
export const pop = payload => ({
type: POP,
...payload,
});
const popToTop = payload => ({
export const popToTop = payload => ({
type: POP_TO_TOP,
...payload,
});
const push = payload => ({
export const push = payload => ({
type: PUSH,
...payload,
});
const reset = payload => ({
export const reset = payload => ({
type: RESET,
key: null,
...payload,
});
const replace = payload => ({
export const replace = payload => ({
type: REPLACE,
...payload,
});
const completeTransition = payload => ({
export const completeTransition = payload => ({
type: COMPLETE_TRANSITION,
preserveFocus: true,
...payload,
});
export default {
POP,
POP_TO_TOP,
PUSH,
RESET,
REPLACE,
COMPLETE_TRANSITION,
pop,
popToTop,
push,
reset,
replace,
completeTransition,
};

View File

@@ -1,5 +1,5 @@
import NavigationActions from '../NavigationActions';
import StackActions from './StackActions';
import * as NavigationActions from '../NavigationActions';
import * as StackActions from './StackActions';
import createConfigGetter from './createConfigGetter';
import getScreenForRouteName from './getScreenForRouteName';
import StateUtils from '../StateUtils';
@@ -577,11 +577,9 @@ export default (routeConfigs, stackConfig = {}) => {
state,
childRoute.key,
route,
// the following tells replaceAt to NOT change the index to this route for the setParam action or complete transition action,
// because people don't expect these actions to switch the active route
action.type === NavigationActions.SET_PARAMS ||
action.type === StackActions.COMPLETE_TRANSITION ||
action.type.includes('DRAWER')
// People don't expect these actions to switch the active route
// TODO: We should switch to action.preserveFocus: true for drawer in future
action.preserveFocus || action.type.includes('DRAWER')
);
}
}

View File

@@ -0,0 +1,11 @@
export const JUMP_TO = 'Navigation/JUMP_TO';
export const jumpTo = (payload: {
routeName: string;
key: string;
params?: object;
}) => ({
type: JUMP_TO,
preserveFocus: true,
...payload,
});

View File

@@ -2,21 +2,13 @@ import invariant from '../utils/invariant';
import getScreenForRouteName from './getScreenForRouteName';
import createConfigGetter from './createConfigGetter';
import NavigationActions from '../NavigationActions';
import StackActions from './StackActions';
import * as NavigationActions from '../NavigationActions';
import * as SwitchActions from './SwitchActions';
import validateRouteConfigMap from './validateRouteConfigMap';
import { createPathParser } from './pathUtils';
const defaultActionCreators = () => ({});
function childrenUpdateWithoutSwitchingIndex(actionType) {
return [
NavigationActions.SET_PARAMS,
// Todo: make SwitchRouter not depend on StackActions..
StackActions.COMPLETE_TRANSITION,
].includes(actionType);
}
export default (routeConfigs, config = {}) => {
// Fail fast on invalid route definitions
validateRouteConfigMap(routeConfigs);
@@ -172,6 +164,43 @@ export default (routeConfigs, config = {}) => {
}
}
if (
action.type === SwitchActions.JUMP_TO &&
(action.key == null || action.key === state.key)
) {
const { params } = action;
const index = state.routes.findIndex(
route => route.routeName === action.routeName
);
if (index === -1) {
throw new Error(
`There is no route named '${
action.routeName
}' in the navigator with the key '${action.key}'.\n` +
`Must be one of: ${state.routes
.map(route => `'${route.routeName}'`)
.join(',')}`
);
}
return getNextState(action, prevState, {
...state,
routes: state.routes.map((route, i) =>
i === index
? {
...route,
params: {
...route.params,
...params,
},
}
: route
),
index,
});
}
// Let the current child handle it
const activeChildLastState = state.routes[state.index];
const activeChildRouter = childRouters[order[state.index]];
@@ -329,9 +358,7 @@ export default (routeConfigs, config = {}) => {
// Nested routers can be updated after switching children with actions such as SET_PARAMS
// and COMPLETE_TRANSITION.
// NOTE: This may be problematic with custom routers because we whitelist the actions
// that can be handled by child routers without automatically changing index.
if (childrenUpdateWithoutSwitchingIndex(action.type)) {
if (action.preserveFocus) {
index = state.index;
}

View File

@@ -5,7 +5,7 @@ import React from 'react';
import SwitchRouter from '../SwitchRouter';
import StackRouter from '../StackRouter';
import TabRouter from '../TabRouter';
import NavigationActions from '../../NavigationActions';
import * as NavigationActions from '../../NavigationActions';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator.ts';
beforeEach(() => {

View File

@@ -6,7 +6,8 @@ import StackRouter from '../StackRouter';
import TabRouter from '../TabRouter';
import SwitchRouter from '../SwitchRouter';
import NavigationActions from '../../NavigationActions';
import * as NavigationActions from '../../NavigationActions';
import * as StackActions from '../StackActions';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator.ts';
beforeEach(() => {
@@ -392,7 +393,8 @@ it('Does not switch tab index when TabRouter child handles COMPLETE_NAVIGATION o
const stateAfterCompleteTransition = TestRouter.getStateForAction(
{
type: NavigationActions.COMPLETE_TRANSITION,
type: StackActions.COMPLETE_TRANSITION,
preserveFocus: true,
key: state2.routes[0].key,
},
state3
@@ -400,6 +402,7 @@ it('Does not switch tab index when TabRouter child handles COMPLETE_NAVIGATION o
const stateAfterSetParams = TestRouter.getStateForAction(
{
type: NavigationActions.SET_PARAMS,
preserveFocus: true,
key: state1.routes[0].routes[0].key,
params: { key: 'value' },
},

View File

@@ -3,8 +3,8 @@
import React from 'react';
import StackRouter from '../StackRouter';
import StackActions from '../StackActions';
import NavigationActions from '../../NavigationActions';
import * as StackActions from '../StackActions';
import * as NavigationActions from '../../NavigationActions';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator.ts';
beforeEach(() => {

View File

@@ -3,7 +3,8 @@
import React from 'react';
import SwitchRouter from '../SwitchRouter';
import StackRouter from '../StackRouter';
import NavigationActions from '../../NavigationActions';
import * as SwitchActions from '../SwitchActions';
import * as NavigationActions from '../../NavigationActions';
describe('SwitchRouter', () => {
it('resets the route when unfocusing a tab by default', () => {
@@ -48,7 +49,7 @@ describe('SwitchRouter', () => {
const router = getExampleRouter();
const state = router.getStateForAction({ type: NavigationActions.INIT });
const state2 = router.getStateForAction(
{ type: NavigationActions.NAVIGATE, routeName: 'B' },
{ type: SwitchActions.JUMP_TO, routeName: 'B' },
state
);
expect(state2.index).toEqual(1);
@@ -68,7 +69,7 @@ describe('SwitchRouter', () => {
expect(state.routeKeyHistory).toBeUndefined();
const state2 = router.getStateForAction(
{ type: NavigationActions.NAVIGATE, routeName: 'B' },
{ type: SwitchActions.JUMP_TO, routeName: 'B' },
state
);
expect(state2.index).toEqual(1);
@@ -87,7 +88,7 @@ describe('SwitchRouter', () => {
expect(
routerHelper.applyAction({
type: NavigationActions.NAVIGATE,
type: SwitchActions.JUMP_TO,
routeName: 'C',
})
).toMatchObject({ index: 2 });
@@ -263,7 +264,7 @@ describe('SwitchRouter', () => {
const state2 = router.getStateForAction(
{
type: NavigationActions.NAVIGATE,
type: SwitchActions.JUMP_TO,
routeName: 'Home',
},
state

View File

@@ -3,7 +3,7 @@
import React from 'react';
import TabRouter from '../TabRouter';
import NavigationActions from '../../NavigationActions';
import * as NavigationActions from '../../NavigationActions';
const INIT_ACTION = { type: NavigationActions.INIT };

View File

@@ -1,4 +1,4 @@
import NavigationActions from '../NavigationActions';
import * as NavigationActions from '../NavigationActions';
import invariant from '../utils/invariant';
const getNavigationActionCreators = route => {

View File

@@ -1,7 +1,7 @@
/* eslint-disable import/no-commonjs */
import pathToRegexp, { compile } from 'path-to-regexp';
import NavigationActions from '../NavigationActions';
import * as NavigationActions from '../NavigationActions';
import invariant from '../utils/invariant';
const queryString = require('query-string');