Skip to content
This repository has been archived by the owner on Feb 8, 2020. It is now read-only.

Commit

Permalink
fix: handle partial initial state better when rehydrating
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Aug 17, 2019
1 parent ca985bb commit 8ed54da
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 58 deletions.
5 changes: 4 additions & 1 deletion packages/core/src/SceneView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
40 changes: 31 additions & 9 deletions packages/core/src/__tests__/__fixtures__/MockRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Router,
CommonAction,
NavigationState,
Route,
DefaultRouterOptions,
} from '../../types';

Expand Down Expand Up @@ -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 }) {
Expand Down
34 changes: 18 additions & 16 deletions packages/core/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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> = {
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/useDescriptors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { NavigationEventEmitter } from './useEventEmitter';
import useNavigationCache from './useNavigationCache';
import {
Descriptor,
PartialState,
NavigationAction,
NavigationHelpers,
NavigationState,
Expand All @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/useNavigationBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
RouteConfig,
Router,
RouterFactory,
PartialState,
} from './types';

/**
Expand Down Expand Up @@ -137,9 +138,9 @@ export default function useNavigationBuilder<
routeNames,
routeParamList,
})
: router.getRehydratedState({
: router.getRehydratedState(currentState as PartialState<State>, {
routeNames,
partialState: currentState as any,
routeParamList,
})
);

Expand Down
19 changes: 19 additions & 0 deletions packages/routers/src/DrawerRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
72 changes: 53 additions & 19 deletions packages/routers/src/StackRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Router,
BaseRouter,
DefaultRouterOptions,
Route,
} from '@navigation-ex/core';

export type StackActionType =
Expand Down Expand Up @@ -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 }) {
Expand Down
45 changes: 36 additions & 9 deletions packages/routers/src/TabRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand Down

0 comments on commit 8ed54da

Please sign in to comment.