mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 09:20:54 +08:00
StackNavigator Replace Action (#3440)
* Navigation replace action The long awaited action to replace the a route in StackNavigator * Fix flow maybe
This commit is contained in:
@@ -32,13 +32,8 @@ class MyNavScreen extends React.Component<MyNavScreenProps> {
|
||||
title="Go to a photos screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.navigate('Profile', {
|
||||
name: 'Dog',
|
||||
headerBackImage: require('./assets/dog-back.png'),
|
||||
})
|
||||
}
|
||||
title="Custom back button"
|
||||
onPress={() => navigation.replace('Profile', { name: 'Lucy' })}
|
||||
title="Replace with profile"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
declare module 'react-navigation' {
|
||||
|
||||
/**
|
||||
* First, a bunch of things we would love to import but instead must
|
||||
* reconstruct (mostly copy-pasted).
|
||||
@@ -68,6 +67,8 @@ declare module 'react-navigation' {
|
||||
|
||||
// The action to run inside the sub-router
|
||||
action?: NavigationNavigateAction,
|
||||
|
||||
key?: string,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationNavigateAction = {|
|
||||
@@ -130,8 +131,9 @@ declare module 'react-navigation' {
|
||||
type: 'Reset',
|
||||
index: number,
|
||||
key?: ?string,
|
||||
actions:
|
||||
Array<NavigationNavigateAction | DeprecatedNavigationNavigateAction>,
|
||||
actions: Array<
|
||||
NavigationNavigateAction | DeprecatedNavigationNavigateAction
|
||||
>,
|
||||
|};
|
||||
|
||||
declare export type NavigationUriAction = {|
|
||||
@@ -144,9 +146,37 @@ declare module 'react-navigation' {
|
||||
uri: string,
|
||||
|};
|
||||
|
||||
declare export type NavigationReplaceAction = {|
|
||||
+type: 'Navigation/REPLACE',
|
||||
+key: string,
|
||||
+routeName: string,
|
||||
+params?: NavigationParams,
|
||||
+action?: NavigationNavigateAction,
|
||||
|};
|
||||
declare export type NavigationPopAction = {|
|
||||
+type: 'Navigation/POP',
|
||||
+n?: number,
|
||||
+immediate?: boolean,
|
||||
|};
|
||||
declare export type NavigationPopToTopAction = {|
|
||||
+type: 'Navigation/POP_TO_TOP',
|
||||
+immediate?: boolean,
|
||||
|};
|
||||
declare export type NavigationPushAction = {|
|
||||
+type: 'Navigation/PUSH',
|
||||
+routeName: string,
|
||||
+params?: NavigationParams,
|
||||
+action?: NavigationNavigateAction,
|
||||
+key?: string,
|
||||
|};
|
||||
|
||||
declare export type NavigationAction =
|
||||
| NavigationInitAction
|
||||
| NavigationNavigateAction
|
||||
| NavigationReplaceAction
|
||||
| NavigationPopAction
|
||||
| NavigationPopToTopAction
|
||||
| NavigationPushAction
|
||||
| NavigationBackAction
|
||||
| NavigationSetParamsAction
|
||||
| NavigationResetAction;
|
||||
@@ -209,9 +239,8 @@ declare module 'react-navigation' {
|
||||
params?: NavigationParams,
|
||||
};
|
||||
|
||||
declare export type NavigationStateRoute =
|
||||
& NavigationLeafRoute
|
||||
& NavigationState;
|
||||
declare export type NavigationStateRoute = NavigationLeafRoute &
|
||||
NavigationState;
|
||||
|
||||
/**
|
||||
* Router
|
||||
@@ -291,12 +320,8 @@ declare module 'react-navigation' {
|
||||
Route: NavigationRoute,
|
||||
Options: {},
|
||||
Props: {}
|
||||
> =
|
||||
& React$ComponentType<NavigationNavigatorProps<Options, Route> & Props>
|
||||
& (
|
||||
| {}
|
||||
| { navigationOptions: NavigationScreenConfig<Options> }
|
||||
);
|
||||
> = React$ComponentType<NavigationNavigatorProps<Options, Route> & Props> &
|
||||
({} | { navigationOptions: NavigationScreenConfig<Options> });
|
||||
|
||||
declare export type NavigationNavigator<
|
||||
State: NavigationState,
|
||||
@@ -334,16 +359,18 @@ declare module 'react-navigation' {
|
||||
|
||||
declare export type HeaderMode = 'float' | 'screen' | 'none';
|
||||
|
||||
declare export type HeaderProps = $Shape<NavigationSceneRendererProps & {
|
||||
mode: HeaderMode,
|
||||
router: NavigationRouter<NavigationState, NavigationStackScreenOptions>,
|
||||
getScreenDetails: NavigationScene => NavigationScreenDetails<
|
||||
NavigationStackScreenOptions
|
||||
>,
|
||||
leftInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
titleInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
rightInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
}>;
|
||||
declare export type HeaderProps = $Shape<
|
||||
NavigationSceneRendererProps & {
|
||||
mode: HeaderMode,
|
||||
router: NavigationRouter<NavigationState, NavigationStackScreenOptions>,
|
||||
getScreenDetails: NavigationScene => NavigationScreenDetails<
|
||||
NavigationStackScreenOptions
|
||||
>,
|
||||
leftInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
titleInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
rightInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
}
|
||||
>;
|
||||
|
||||
/**
|
||||
* Stack Navigator
|
||||
@@ -493,6 +520,18 @@ declare module 'react-navigation' {
|
||||
eventName: string,
|
||||
callback: NavigationEventCallback
|
||||
) => NavigationEventSubscription,
|
||||
push: (
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
action?: NavigationNavigateAction
|
||||
) => boolean,
|
||||
replace: (
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
action?: NavigationNavigateAction
|
||||
) => boolean,
|
||||
pop: (n?: number, params?: { immediate?: boolean }) => boolean,
|
||||
popToTop: (params?: { immediate?: boolean }) => boolean,
|
||||
};
|
||||
|
||||
declare export type NavigationNavigatorProps<O: {}, S: {}> = $Shape<{
|
||||
@@ -767,7 +806,7 @@ declare module 'react-navigation' {
|
||||
>(
|
||||
router: NavigationRouter<S, O>,
|
||||
routeConfigs?: NavigationRouteConfigMap,
|
||||
navigatorConfig?: NavigatorConfig,
|
||||
navigatorConfig?: NavigatorConfig
|
||||
): _NavigatorCreator<NavigationViewProps, S, O>;
|
||||
|
||||
declare export function StackNavigator(
|
||||
|
||||
@@ -5,6 +5,7 @@ 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 SET_PARAMS = 'Navigation/SET_PARAMS';
|
||||
const URI = 'Navigation/URI';
|
||||
const COMPLETE_TRANSITION = 'Navigation/COMPLETE_TRANSITION';
|
||||
@@ -79,6 +80,15 @@ const reset = createAction(RESET, payload => ({
|
||||
actions: payload.actions,
|
||||
}));
|
||||
|
||||
const replace = createAction(REPLACE, payload => ({
|
||||
type: REPLACE,
|
||||
key: payload.key,
|
||||
params: payload.params,
|
||||
action: payload.action,
|
||||
routeName: payload.routeName,
|
||||
immediate: payload.immediate,
|
||||
}));
|
||||
|
||||
const setParams = createAction(SET_PARAMS, payload => ({
|
||||
type: SET_PARAMS,
|
||||
key: payload.key,
|
||||
@@ -157,6 +167,7 @@ export default {
|
||||
POP_TO_TOP,
|
||||
PUSH,
|
||||
RESET,
|
||||
REPLACE,
|
||||
SET_PARAMS,
|
||||
URI,
|
||||
COMPLETE_TRANSITION,
|
||||
@@ -169,6 +180,7 @@ export default {
|
||||
popToTop,
|
||||
push,
|
||||
reset,
|
||||
replace,
|
||||
setParams,
|
||||
uri,
|
||||
completeTransition,
|
||||
|
||||
@@ -65,5 +65,15 @@ export default function(navigation) {
|
||||
navigation.dispatch(
|
||||
NavigationActions.push({ routeName, params, action })
|
||||
),
|
||||
|
||||
replace: (routeName, params, action) =>
|
||||
navigation.dispatch(
|
||||
NavigationActions.replace({
|
||||
routeName,
|
||||
params,
|
||||
action,
|
||||
key: navigation.state.key,
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
@@ -337,6 +338,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
|
||||
@@ -171,7 +171,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
//Handle pop-to-top behavior. Make sure this happens after children have had a chance to handle the action, so that the inner stack pops to top first.
|
||||
// Handle pop-to-top behavior. Make sure this happens after children have had a chance to handle the action, so that the inner stack pops to top first.
|
||||
if (action.type === NavigationActions.POP_TO_TOP) {
|
||||
if (state.index !== 0) {
|
||||
return {
|
||||
@@ -183,6 +183,31 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Handle replace action
|
||||
if (action.type === NavigationActions.REPLACE) {
|
||||
const 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];
|
||||
let childState = {};
|
||||
if (childRouter) {
|
||||
const childAction =
|
||||
action.action ||
|
||||
NavigationActions.init({ params: action.params });
|
||||
childState = childRouter.getStateForAction(childAction);
|
||||
}
|
||||
const routes = [...state.routes];
|
||||
routes[routeIndex] = {
|
||||
params: action.params,
|
||||
// merge the child state in this order to allow params override
|
||||
...childState,
|
||||
key: action.key,
|
||||
routeName: action.routeName,
|
||||
};
|
||||
return { ...state, routes };
|
||||
}
|
||||
}
|
||||
|
||||
// Handle explicit push navigation action. Make sure this happens after children have had a chance to handle the action
|
||||
if (
|
||||
behavesLikePushAction(action) &&
|
||||
@@ -235,6 +260,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
action.action || NavigationActions.init({ params: action.params });
|
||||
route = {
|
||||
params: action.params,
|
||||
// merge the child state in this order to allow params override
|
||||
...childRouter.getStateForAction(childAction),
|
||||
key,
|
||||
routeName: action.routeName,
|
||||
|
||||
@@ -515,6 +515,28 @@ describe('StackRouter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Replace action works', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
bar: { screen: () => <div /> },
|
||||
});
|
||||
const initState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'foo' })
|
||||
);
|
||||
const replacedState = TestRouter.getStateForAction(
|
||||
NavigationActions.replace({
|
||||
routeName: 'bar',
|
||||
params: { meaning: 42 },
|
||||
key: initState.routes[0].key,
|
||||
}),
|
||||
initState
|
||||
);
|
||||
expect(replacedState.index).toEqual(0);
|
||||
expect(replacedState.routes.length).toEqual(1);
|
||||
expect(replacedState.routes[0].routeName).toEqual('bar');
|
||||
expect(replacedState.routes[0].params.meaning).toEqual(42);
|
||||
});
|
||||
|
||||
test('Handles push transition logic with completion action', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
|
||||
@@ -240,6 +240,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"key": "s1",
|
||||
|
||||
Reference in New Issue
Block a user