From 81348959eef5ed7755d6de0bd12e55d72b2a0948 Mon Sep 17 00:00:00 2001 From: "satyajit.happy" Date: Mon, 12 Aug 2019 06:11:10 +0530 Subject: [PATCH] fix: use correct dispatch in methods in screen's navigation prop --- packages/core/src/useDescriptors.tsx | 4 ++ packages/core/src/useNavigationBuilder.tsx | 1 + packages/core/src/useNavigationCache.tsx | 47 ++++++++++++++------- packages/core/src/useNavigationHelpers.tsx | 48 ++++++++++------------ 4 files changed, 60 insertions(+), 40 deletions(-) diff --git a/packages/core/src/useDescriptors.tsx b/packages/core/src/useDescriptors.tsx index e6c8069c..cce7739a 100644 --- a/packages/core/src/useDescriptors.tsx +++ b/packages/core/src/useDescriptors.tsx @@ -13,6 +13,7 @@ import { NavigationState, ParamListBase, RouteConfig, + Router, } from './types'; type Options = { @@ -29,6 +30,7 @@ type Options = { addActionListener: (listener: ChildActionListener) => void; removeActionListener: (listener: ChildActionListener) => void; onRouteFocus: (key: string) => void; + router: Router; emitter: NavigationEventEmitter; }; @@ -54,6 +56,7 @@ export default function useDescriptors< addActionListener, removeActionListener, onRouteFocus, + router, emitter, }: Options) { const [options, setOptions] = React.useState<{ [key: string]: object }>({}); @@ -79,6 +82,7 @@ export default function useDescriptors< getState, navigation, setOptions, + router, emitter, }); diff --git a/packages/core/src/useNavigationBuilder.tsx b/packages/core/src/useNavigationBuilder.tsx index 7eb7e2c4..dc973718 100644 --- a/packages/core/src/useNavigationBuilder.tsx +++ b/packages/core/src/useNavigationBuilder.tsx @@ -233,6 +233,7 @@ export default function useNavigationBuilder< onRouteFocus, addActionListener, removeActionListener, + router, emitter, }); diff --git a/packages/core/src/useNavigationCache.tsx b/packages/core/src/useNavigationCache.tsx index 9a0a6d39..2c46f2e4 100644 --- a/packages/core/src/useNavigationCache.tsx +++ b/packages/core/src/useNavigationCache.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import * as BaseActions from './BaseActions'; import { NavigationEventEmitter } from './useEventEmitter'; import { NavigationAction, @@ -6,15 +7,18 @@ import { NavigationProp, ParamListBase, NavigationState, + Router, } from './types'; type Options = { state: NavigationState; getState: () => NavigationState; - navigation: NavigationHelpers; + navigation: NavigationHelpers & + Partial>; setOptions: ( cb: (options: { [key: string]: object }) => { [key: string]: object } ) => void; + router: Router; emitter: NavigationEventEmitter; }; @@ -33,16 +37,21 @@ type NavigationCache< export default function useNavigationCache< State extends NavigationState, ScreenOptions extends object ->({ state, getState, navigation, setOptions, emitter }: Options) { +>({ state, getState, navigation, setOptions, router, emitter }: Options) { // Cache object which holds navigation objects for each screen // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change // In reality, these deps will rarely change, if ever const cache = React.useMemo( () => ({ current: {} as NavigationCache }), // eslint-disable-next-line react-hooks/exhaustive-deps - [getState, navigation, setOptions, emitter] + [getState, navigation, setOptions, router, emitter] ); + const actions = { + ...router.actionCreators, + ...BaseActions, + }; + cache.current = state.routes.reduce>( (acc, route, index) => { const previous = cache.current[route.key]; @@ -56,19 +65,29 @@ export default function useNavigationCache< // eslint-disable-next-line @typescript-eslint/no-unused-vars const { emit, ...rest } = navigation; + const dispatch = ( + action: NavigationAction | ((state: State) => State) + ) => + navigation.dispatch( + typeof action === 'object' && action != null + ? { source: route.key, ...action } + : action + ); + + const helpers = Object.keys(actions).reduce( + (acc, name) => { + // @ts-ignore + acc[name] = (...args: any) => dispatch(actions[name](...args)); + return acc; + }, + {} as { [key: string]: () => void } + ); + acc[route.key] = { ...rest, + ...helpers, ...emitter.create(route.key), - dispatch: ( - action: - | NavigationAction - | ((state: NavigationState) => NavigationState) - ) => - navigation.dispatch( - typeof action === 'object' && action != null - ? { source: route.key, ...action } - : action - ), + dispatch, setOptions: (options: object) => setOptions(o => ({ ...o, @@ -86,7 +105,7 @@ export default function useNavigationCache< return navigation ? navigation.isFocused() : true; }, isFirstRouteInParent: () => isFirst, - } as NavigationProp; + }; } return acc; diff --git a/packages/core/src/useNavigationHelpers.tsx b/packages/core/src/useNavigationHelpers.tsx index 3cc95710..0c021182 100644 --- a/packages/core/src/useNavigationHelpers.tsx +++ b/packages/core/src/useNavigationHelpers.tsx @@ -12,36 +12,30 @@ import { Router, } from './types'; -type Options = { +type Options = { onAction: ( action: NavigationAction, visitedNavigators?: Set ) => boolean; - getState: () => NavigationState; - setState: (state: NavigationState) => void; + getState: () => State; + setState: (state: State) => void; emitter: NavigationEventEmitter; - router: Router; + router: Router; }; /** * Navigation object with helper methods to be used by a navigator. * This object includes methods for common actions as well as methods the parent screen's navigation object. */ -export default function useNavigationHelpers({ - onAction, - getState, - setState, - emitter, - router, -}: Options) { +export default function useNavigationHelpers< + State extends NavigationState, + Action extends NavigationAction +>({ onAction, getState, setState, emitter, router }: Options) { const parentNavigationHelpers = React.useContext(NavigationContext); const { performTransaction } = React.useContext(NavigationStateContext); - return React.useMemo((): NavigationHelpers & - Partial> => { - const dispatch = ( - action: NavigationAction | ((state: NavigationState) => NavigationState) - ) => { + return React.useMemo(() => { + const dispatch = (action: Action | ((state: State) => State)) => { performTransaction(() => { if (typeof action === 'function') { setState(action(getState())); @@ -56,17 +50,18 @@ export default function useNavigationHelpers({ ...BaseActions, }; - // @ts-ignore + const helpers = Object.keys(actions).reduce( + (acc, name) => { + // @ts-ignore + acc[name] = (...args: any) => dispatch(actions[name](...args)); + return acc; + }, + {} as { [key: string]: () => void } + ); + return { ...parentNavigationHelpers, - ...Object.keys(actions).reduce( - (acc, name) => { - // @ts-ignore - acc[name] = (...args: any) => dispatch(actions[name](...args)); - return acc; - }, - {} as { [key: string]: () => void } - ), + ...helpers, dispatch, emit: emitter.emit, isFocused: parentNavigationHelpers @@ -76,7 +71,8 @@ export default function useNavigationHelpers({ router.canGoBack(getState()) || (parentNavigationHelpers && parentNavigationHelpers.canGoBack()) || false, - }; + } as NavigationHelpers & + (NavigationProp | undefined); }, [ router, getState,