mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-04-25 21:15:26 +08:00
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:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
11
packages/core/src/routers/SwitchActions.ts
Normal file
11
packages/core/src/routers/SwitchActions.ts
Normal 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,
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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' },
|
||||
},
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import NavigationActions from '../NavigationActions';
|
||||
import * as NavigationActions from '../NavigationActions';
|
||||
import invariant from '../utils/invariant';
|
||||
|
||||
const getNavigationActionCreators = route => {
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user