diff --git a/example/StackNavigator.tsx b/example/StackNavigator.tsx index b725196a..06da173d 100644 --- a/example/StackNavigator.tsx +++ b/example/StackNavigator.tsx @@ -175,6 +175,7 @@ const StackRouter: Router = { ], }; } + return null; case 'REPLACE': { @@ -192,6 +193,36 @@ const StackRouter: Router = { }; } + case 'UPDATE': { + if ( + action.payload.name !== undefined && + !state.routeNames.includes(action.payload.name) + ) { + return null; + } + + const index = + action.payload.key !== undefined + ? state.routes.findIndex(route => route.key === action.payload.key) + : state.index; + + if (index === -1) { + return null; + } + + return { + ...state, + routes: [ + ...state.routes.slice(0, index), + { + ...state.routes[index], + ...action.payload, + }, + ...state.routes.slice(index + 1), + ], + }; + } + case 'GO_BACK': return StackRouter.getStateForAction(state, { type: 'POP', diff --git a/example/TabNavigator.tsx b/example/TabNavigator.tsx index 6f899944..c8f95ae4 100644 --- a/example/TabNavigator.tsx +++ b/example/TabNavigator.tsx @@ -117,18 +117,33 @@ const TabRouter: Router = { return null; - case 'REPLACE': { + case 'UPDATE': { + if ( + action.payload.name !== undefined && + !state.routeNames.includes(action.payload.name) + ) { + return null; + } + + const index = + action.payload.key !== undefined + ? state.routes.findIndex(route => route.key === action.payload.key) + : state.index; + + if (index === -1) { + return null; + } + return { ...state, - routes: state.routes.map((route, i) => - i === state.index - ? { - key: `${action.payload.name}-${shortid()}`, - name: action.payload.name, - params: action.payload.params, - } - : route - ), + routes: [ + ...state.routes.slice(0, index), + { + ...state.routes[index], + ...action.payload, + }, + ...state.routes.slice(index + 1), + ], }; } diff --git a/example/index.tsx b/example/index.tsx index 66980eac..9bcd622f 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -150,7 +150,7 @@ function App() { name="first" component={First} options={{ title: 'Foo' }} - defaultParams={{ author: 'Jane' }} + initialParams={{ author: 'Jane' }} /> } | { type: 'RESET'; payload: PartialState & { key?: string }; @@ -27,6 +28,10 @@ export function replace(name: string, params?: object): Action { return { type: 'REPLACE', payload: { name, params } }; } +export function update(route: Partial): Action { + return { type: 'UPDATE', payload: route }; +} + export function reset(state: PartialState & { key?: string }): Action { return { type: 'RESET', payload: state }; } diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx index 11eec4b1..d246fd44 100644 --- a/src/__tests__/index.test.tsx +++ b/src/__tests__/index.test.tsx @@ -115,7 +115,7 @@ it('initializes state for a navigator on navigation', () => { diff --git a/src/types.tsx b/src/types.tsx index 975fe963..d346e39d 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -104,9 +104,17 @@ export type Router = { action: Action ): NavigationState | null; + /** + * Update state for a child navigator and focus it + * + * @param state State object to apply the action on. + * @param options.update Updated navigation state for the child navigator. + * @param options.focus Whether to focus the new child. + * @param options.key Route key of the child to update. + */ getStateForChildUpdate( state: NavigationState, - payload: { + options: { update: NavigationState; focus: boolean; key: string | undefined; @@ -116,21 +124,17 @@ export type Router = { /** * Whether the action bubbles to other navigators * When an action isn't handled by current navigator, it can be passed to nested navigators + * + * @param action Action object to check. */ - shouldActionPropagateToChildren( - action: NavigationAction, - navigatorKey: string, - sourceNavigatorKey?: string - ): boolean; + shouldActionPropagateToChildren(action: NavigationAction): boolean; /** * Whether the action should also change focus in parent navigator + * + * @param action Action object to check. */ - shouldActionChangeFocus( - action: NavigationAction, - navigatorKey: string, - sourceNavigatorKey?: string - ): boolean; + shouldActionChangeFocus(action: NavigationAction): boolean; /** * Action creators for the router. @@ -186,6 +190,17 @@ export type NavigationHelpers< : [RouteName, ParamList[RouteName]] ): void; + /** + * Update properties for a route, for example: updating params. + * If a key is provided, the route with matching key will be updated. + * If no key is provided, the focused route will be updated. + * + * @param route New properties for the route object. + */ + update( + route: Partial> + ): void; + /** * Reset the navigation state to the provided state. * If a key is provided, the state with matching key will be reset. @@ -258,9 +273,9 @@ export type RouteConfig< options?: Options; /** - * Default params object for the route. + * Initial params object for the route. */ - defaultParams?: ParamList[RouteName]; + initialParams?: ParamList[RouteName]; } & ( | { /** diff --git a/src/useNavigationBuilder.tsx b/src/useNavigationBuilder.tsx index d0127d55..1e8db95d 100644 --- a/src/useNavigationBuilder.tsx +++ b/src/useNavigationBuilder.tsx @@ -55,7 +55,7 @@ export default function useNavigationBuilder( : routeNames[0]; const initialParamsList = routeNames.reduce( (acc, curr) => { - acc[curr] = screens[curr].defaultParams; + acc[curr] = screens[curr].initialParams; return acc; }, {} as { [key: string]: object | undefined } diff --git a/src/useOnAction.tsx b/src/useOnAction.tsx index c5ee8099..8470fa6f 100644 --- a/src/useOnAction.tsx +++ b/src/useOnAction.tsx @@ -41,11 +41,7 @@ export default function useOnAction({ if (result !== null) { if (handleChildUpdateParent) { - const shouldFocus = router.shouldActionChangeFocus( - action, - state.key, - sourceNavigatorKey - ); + const shouldFocus = router.shouldActionChangeFocus(action); handleChildUpdateParent(result, shouldFocus, key); } else if (state !== result) { @@ -62,13 +58,7 @@ export default function useOnAction({ } } - if ( - router.shouldActionPropagateToChildren( - action, - state.key, - sourceNavigatorKey - ) - ) { + if (router.shouldActionPropagateToChildren(action)) { for (let i = actionListeners.length - 1; i >= 0; i--) { const listener = actionListeners[i];