mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-03-06 17:34:59 +08:00
fix: handle partial initial state better when rehydrating
This commit is contained in:
committed by
Satyajit Sahoo
parent
ca985bb96a
commit
8ed54dace4
@@ -9,12 +9,15 @@ import {
|
||||
NavigationState,
|
||||
NavigationProp,
|
||||
RouteConfig,
|
||||
PartialState,
|
||||
} from './types';
|
||||
|
||||
type Props<State extends NavigationState, ScreenOptions extends object> = {
|
||||
screen: RouteConfig<ParamListBase, string, ScreenOptions>;
|
||||
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
||||
route: Route<string> & { state?: NavigationState };
|
||||
route: Route<string> & {
|
||||
state?: NavigationState | PartialState<NavigationState>;
|
||||
};
|
||||
getState: () => NavigationState;
|
||||
setState: (state: NavigationState) => void;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Router,
|
||||
CommonAction,
|
||||
NavigationState,
|
||||
Route,
|
||||
DefaultRouterOptions,
|
||||
} from '../../types';
|
||||
|
||||
@@ -30,19 +31,40 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
};
|
||||
},
|
||||
|
||||
getRehydratedState({ routeNames, partialState }) {
|
||||
getRehydratedState(partialState, { routeNames, routeParamList }) {
|
||||
let state = partialState;
|
||||
|
||||
if (state.stale) {
|
||||
state = {
|
||||
...state,
|
||||
stale: false,
|
||||
routeNames,
|
||||
key: String(MockRouterKey.current++),
|
||||
};
|
||||
if (!state.stale) {
|
||||
return state as NavigationState;
|
||||
}
|
||||
|
||||
return state;
|
||||
const routes = state.routes
|
||||
.filter(route => routeNames.includes(route.name))
|
||||
.map(
|
||||
route =>
|
||||
({
|
||||
...route,
|
||||
key: route.key || `${route.name}-${MockRouterKey.current++}`,
|
||||
params:
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
...routeParamList[route.name],
|
||||
...route.params,
|
||||
}
|
||||
: route.params,
|
||||
} as Route<string>)
|
||||
);
|
||||
|
||||
return {
|
||||
stale: false,
|
||||
key: String(MockRouterKey.current++),
|
||||
index:
|
||||
typeof state.index === 'number' && state.index < routes.length
|
||||
? state.index
|
||||
: 0,
|
||||
routeNames,
|
||||
routes,
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames }) {
|
||||
|
||||
@@ -27,19 +27,17 @@ export type NavigationState = {
|
||||
stale?: false;
|
||||
};
|
||||
|
||||
export type InitialState = Omit<
|
||||
NavigationState,
|
||||
'key' | 'routes' | 'routeNames'
|
||||
> & {
|
||||
key?: string;
|
||||
routeNames?: string[];
|
||||
routes: Array<Route<string> & { state?: InitialState }>;
|
||||
export type InitialState = Partial<Omit<NavigationState, 'routes'>> & {
|
||||
routes: Array<Omit<Route<string>, 'key'> & { state?: InitialState }>;
|
||||
};
|
||||
|
||||
export type PartialState<State extends NavigationState> = State & {
|
||||
stale: true;
|
||||
key?: undefined;
|
||||
routeNames?: undefined;
|
||||
export type PartialState<State extends NavigationState> = Partial<
|
||||
Omit<State, 'key' | 'routes' | 'routeNames'>
|
||||
> & {
|
||||
stale: boolean;
|
||||
routes: Array<
|
||||
Omit<Route<string>, 'key'> & { key?: string; state?: InitialState }
|
||||
>;
|
||||
};
|
||||
|
||||
export type Route<RouteName extends string> = {
|
||||
@@ -122,13 +120,17 @@ export type Router<
|
||||
/**
|
||||
* Rehydrate the full navigation state from a given partial state.
|
||||
*
|
||||
* @param partialState Navigation state to rehydrate from.
|
||||
* @param options.routeNames List of valid route names as defined in the screen components.
|
||||
* @param options.partialState Navigation state to rehydrate from.
|
||||
* @param options.routeParamsList Object containing params for each route.
|
||||
*/
|
||||
getRehydratedState(options: {
|
||||
routeNames: string[];
|
||||
partialState: State | PartialState<State>;
|
||||
}): State;
|
||||
getRehydratedState(
|
||||
partialState: PartialState<State>,
|
||||
options: {
|
||||
routeNames: string[];
|
||||
routeParamList: ParamListBase;
|
||||
}
|
||||
): State;
|
||||
|
||||
/**
|
||||
* Take the current state and updated list of route names, and return a new state.
|
||||
|
||||
@@ -8,7 +8,6 @@ import { NavigationEventEmitter } from './useEventEmitter';
|
||||
import useNavigationCache from './useNavigationCache';
|
||||
import {
|
||||
Descriptor,
|
||||
PartialState,
|
||||
NavigationAction,
|
||||
NavigationHelpers,
|
||||
NavigationState,
|
||||
@@ -18,7 +17,7 @@ import {
|
||||
} from './types';
|
||||
|
||||
type Options<ScreenOptions extends object> = {
|
||||
state: NavigationState | PartialState<NavigationState>;
|
||||
state: NavigationState;
|
||||
screens: { [key: string]: RouteConfig<ParamListBase, string, ScreenOptions> };
|
||||
navigation: NavigationHelpers<ParamListBase>;
|
||||
screenOptions?: ScreenOptions;
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
RouteConfig,
|
||||
Router,
|
||||
RouterFactory,
|
||||
PartialState,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
@@ -137,9 +138,9 @@ export default function useNavigationBuilder<
|
||||
routeNames,
|
||||
routeParamList,
|
||||
})
|
||||
: router.getRehydratedState({
|
||||
: router.getRehydratedState(currentState as PartialState<State>, {
|
||||
routeNames,
|
||||
partialState: currentState as any,
|
||||
routeParamList,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -68,6 +68,25 @@ export default function DrawerRouter(
|
||||
};
|
||||
},
|
||||
|
||||
getRehydratedState(partialState, { routeNames, routeParamList }) {
|
||||
if (!partialState.stale) {
|
||||
return partialState as DrawerNavigationState;
|
||||
}
|
||||
|
||||
const state = router.getRehydratedState(partialState, {
|
||||
routeNames,
|
||||
routeParamList,
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen:
|
||||
typeof partialState.isDrawerOpen === 'boolean'
|
||||
? partialState.isDrawerOpen
|
||||
: false,
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Router,
|
||||
BaseRouter,
|
||||
DefaultRouterOptions,
|
||||
Route,
|
||||
} from '@navigation-ex/core';
|
||||
|
||||
export type StackActionType =
|
||||
@@ -47,36 +48,69 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
...BaseRouter,
|
||||
|
||||
getInitialState({ routeNames, routeParamList }) {
|
||||
const index =
|
||||
options.initialRouteName === undefined
|
||||
? 0
|
||||
: routeNames.indexOf(options.initialRouteName);
|
||||
const initialRouteName =
|
||||
options.initialRouteName !== undefined
|
||||
? options.initialRouteName
|
||||
: routeNames[0];
|
||||
|
||||
return {
|
||||
key: `stack-${shortid()}`,
|
||||
index,
|
||||
index: 0,
|
||||
routeNames,
|
||||
routes: routeNames.slice(0, index + 1).map(name => ({
|
||||
name,
|
||||
key: `${name}-${shortid()}`,
|
||||
params: routeParamList[name],
|
||||
})),
|
||||
routes: [
|
||||
{
|
||||
key: `${initialRouteName}-${shortid()}`,
|
||||
name: initialRouteName,
|
||||
params: routeParamList[initialRouteName],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
getRehydratedState({ routeNames, partialState }) {
|
||||
getRehydratedState(partialState, { routeNames, routeParamList }) {
|
||||
let state = partialState;
|
||||
|
||||
if (state.stale) {
|
||||
state = {
|
||||
...state,
|
||||
stale: false,
|
||||
routeNames,
|
||||
key: `stack-${shortid()}`,
|
||||
};
|
||||
if (!state.stale) {
|
||||
return state as StackNavigationState;
|
||||
}
|
||||
|
||||
return state;
|
||||
const routes = state.routes
|
||||
.filter(route => routeNames.includes(route.name))
|
||||
.map(
|
||||
route =>
|
||||
({
|
||||
...route,
|
||||
key: route.key || `${route.name}-${shortid()}`,
|
||||
params:
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
...routeParamList[route.name],
|
||||
...route.params,
|
||||
}
|
||||
: route.params,
|
||||
} as Route<string>)
|
||||
);
|
||||
|
||||
if (routes.length === 0) {
|
||||
const initialRouteName =
|
||||
options.initialRouteName !== undefined
|
||||
? options.initialRouteName
|
||||
: routeNames[0];
|
||||
|
||||
routes.push({
|
||||
key: `${initialRouteName}-${shortid()}`,
|
||||
name: initialRouteName,
|
||||
params: routeParamList[initialRouteName],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
stale: false,
|
||||
key: `stack-${shortid()}`,
|
||||
index: routes.length - 1,
|
||||
routeNames,
|
||||
routes,
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames }) {
|
||||
|
||||
@@ -71,19 +71,46 @@ export default function TabRouter({
|
||||
};
|
||||
},
|
||||
|
||||
getRehydratedState({ routeNames, partialState }) {
|
||||
getRehydratedState(partialState, { routeNames, routeParamList }) {
|
||||
let state = partialState;
|
||||
|
||||
if (state.stale) {
|
||||
state = {
|
||||
...state,
|
||||
stale: false,
|
||||
routeNames,
|
||||
key: `tab-${shortid()}`,
|
||||
};
|
||||
if (!state.stale) {
|
||||
return state as TabNavigationState;
|
||||
}
|
||||
|
||||
return state;
|
||||
const routes = routeNames.map(name => {
|
||||
const route = state.routes.find(r => r.name === name);
|
||||
|
||||
return {
|
||||
name,
|
||||
key: route && route.key ? route.key : `${name}-${shortid()}`,
|
||||
params:
|
||||
routeParamList[name] !== undefined
|
||||
? {
|
||||
...routeParamList[name],
|
||||
...(route ? route.params : undefined),
|
||||
}
|
||||
: route
|
||||
? route.params
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
const routeKeyHistory = state.routeKeyHistory
|
||||
? state.routeKeyHistory.filter(key => routes.find(r => r.key === key))
|
||||
: [];
|
||||
|
||||
return {
|
||||
stale: false,
|
||||
key: `tab-${shortid()}`,
|
||||
index:
|
||||
typeof state.index === 'number' && state.index < routes.length
|
||||
? state.index
|
||||
: 0,
|
||||
routeNames,
|
||||
routeKeyHistory,
|
||||
routes,
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames, routeParamList }) {
|
||||
|
||||
Reference in New Issue
Block a user