fix: add missing helper types in descriptors

This commit is contained in:
Satyajit Sahoo
2020-11-12 03:54:41 +01:00
parent 4cad132c2c
commit 21a11543bf
11 changed files with 138 additions and 122 deletions

View File

@@ -153,15 +153,12 @@ export type BottomTabNavigationOptions = {
};
export type BottomTabDescriptor = Descriptor<
ParamListBase,
string,
TabNavigationState<ParamListBase>,
BottomTabNavigationOptions
BottomTabNavigationOptions,
BottomTabNavigationProp<ParamListBase>,
RouteProp<ParamListBase, string>
>;
export type BottomTabDescriptorMap = {
[key: string]: BottomTabDescriptor;
};
export type BottomTabDescriptorMap = Record<string, BottomTabDescriptor>;
export type BottomTabNavigationConfig<T = BottomTabBarOptions> = {
/**

View File

@@ -318,11 +318,9 @@ export type CompositeNavigationProp<
>;
export type Descriptor<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string,
State extends NavigationState = NavigationState,
ScreenOptions extends {} = {},
EventMap extends EventMapBase = {}
ScreenOptions extends {},
Navigation extends NavigationProp<any, any, any, any, any>,
Route extends RouteProp<any, any>
> = {
/**
* Render the component associated with this route.
@@ -337,18 +335,12 @@ export type Descriptor<
/**
* Route object for the screen
*/
route: RouteProp<ParamList, RouteName>;
route: Route;
/**
* Navigation object for the screen
*/
navigation: NavigationProp<
ParamList,
RouteName,
State,
ScreenOptions,
EventMap
>;
navigation: Navigation;
};
export type ScreenListeners<

View File

@@ -1,14 +1,24 @@
import * as React from 'react';
import type { NavigationState, ParamListBase } from '@react-navigation/routers';
import CurrentRenderContext from './CurrentRenderContext';
import type { Descriptor, NavigationHelpers } from './types';
import type {
Descriptor,
NavigationHelpers,
NavigationProp,
RouteProp,
} from './types';
type Options = {
state: NavigationState;
navigation: NavigationHelpers<ParamListBase>;
descriptors: {
[key: string]: Descriptor<ParamListBase, string, NavigationState, object>;
};
descriptors: Record<
string,
Descriptor<
object,
NavigationProp<ParamListBase>,
RouteProp<ParamListBase, string>
>
>;
};
/**

View File

@@ -21,6 +21,7 @@ import type {
RouteConfig,
RouteProp,
EventMapBase,
NavigationProp,
} from './types';
type Options<
@@ -50,7 +51,7 @@ type Options<
addKeyedListener: AddKeyedListener;
onRouteFocus: (key: string) => void;
router: Router<State, NavigationAction>;
emitter: NavigationEventEmitter<any>;
emitter: NavigationEventEmitter<EventMap>;
};
/**
@@ -63,6 +64,7 @@ type Options<
*/
export default function useDescriptors<
State extends NavigationState,
ActionHelpers extends Record<string, () => void>,
ScreenOptions extends {},
EventMap extends EventMapBase
>({
@@ -105,7 +107,7 @@ export default function useDescriptors<
]
);
const navigations = useNavigationCache<State, ScreenOptions>({
const navigations = useNavigationCache<State, ScreenOptions, EventMap>({
state,
getState,
navigation,
@@ -117,7 +119,15 @@ export default function useDescriptors<
const routes = useRouteCache(state.routes);
return routes.reduce<
Record<string, Descriptor<ParamListBase, string, State, ScreenOptions>>
Record<
string,
Descriptor<
ScreenOptions,
NavigationProp<ParamListBase, string, State, ScreenOptions, EventMap> &
ActionHelpers,
RouteProp<ParamListBase, string>
>
>
>((acc, route, i) => {
const screen = screens[route.name];
const navigation = navigations[route.key];
@@ -145,6 +155,7 @@ export default function useDescriptors<
acc[route.key] = {
route,
// @ts-expect-error: it's missing action helpers, fix later
navigation,
render() {
return (

View File

@@ -533,7 +533,12 @@ export default function useNavigationBuilder<
getStateListeners: keyedListeners.getState,
});
const descriptors = useDescriptors<State, ScreenOptions, EventMap>({
const descriptors = useDescriptors<
State,
ActionHelpers,
ScreenOptions,
EventMap
>({
state,
screens,
navigation,
@@ -545,6 +550,7 @@ export default function useNavigationBuilder<
addListener,
addKeyedListener,
router,
// @ts-expect-error: this should have both core and custom events, but too much work right now
emitter,
});

View File

@@ -8,9 +8,12 @@ import {
} from '@react-navigation/routers';
import type { NavigationEventEmitter } from './useEventEmitter';
import type { EventMapBase, NavigationHelpers, NavigationProp } from './types';
import type { NavigationHelpers, NavigationProp } from './types';
type Options<State extends NavigationState> = {
type Options<
State extends NavigationState,
EventMap extends Record<string, any>
> = {
state: State;
getState: () => State;
navigation: NavigationHelpers<ParamListBase> &
@@ -19,15 +22,17 @@ type Options<State extends NavigationState> = {
cb: (options: Record<string, object>) => Record<string, object>
) => void;
router: Router<State, NavigationAction>;
emitter: NavigationEventEmitter<EventMapBase>;
emitter: NavigationEventEmitter<EventMap>;
};
type NavigationCache<
State extends NavigationState,
ScreenOptions extends {}
> = {
[key: string]: NavigationProp<ParamListBase, string, State, ScreenOptions>;
};
ScreenOptions extends {},
EventMap extends Record<string, any>
> = Record<
string,
NavigationProp<ParamListBase, string, State, ScreenOptions, EventMap>
>;
/**
* Hook to cache navigation objects for each screen in the navigator.
@@ -36,7 +41,8 @@ type NavigationCache<
*/
export default function useNavigationCache<
State extends NavigationState,
ScreenOptions extends {}
ScreenOptions extends {},
EventMap extends Record<string, any>
>({
state,
getState,
@@ -44,12 +50,12 @@ export default function useNavigationCache<
setOptions,
router,
emitter,
}: Options<State>) {
}: Options<State, EventMap>) {
// 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<State, ScreenOptions> }),
() => ({ current: {} as NavigationCache<State, ScreenOptions, EventMap> }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[getState, navigation, setOptions, router, emitter]
);
@@ -59,67 +65,67 @@ export default function useNavigationCache<
...CommonActions,
};
cache.current = state.routes.reduce<NavigationCache<State, ScreenOptions>>(
(acc, route) => {
const previous = cache.current[route.key];
cache.current = state.routes.reduce<
NavigationCache<State, ScreenOptions, EventMap>
>((acc, route) => {
const previous = cache.current[route.key];
if (previous) {
// If a cached navigation object already exists, reuse it
acc[route.key] = previous;
} else {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { emit, ...rest } = navigation;
if (previous) {
// If a cached navigation object already exists, reuse it
acc[route.key] = previous;
} else {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { emit, ...rest } = navigation;
const dispatch = (
action: NavigationAction | ((state: State) => NavigationAction)
) => {
const payload =
typeof action === 'function' ? action(getState()) : action;
const dispatch = (
action: NavigationAction | ((state: State) => NavigationAction)
) => {
const payload =
typeof action === 'function' ? action(getState()) : action;
navigation.dispatch(
typeof payload === 'object' && payload != null
? { source: route.key, ...payload }
: payload
);
};
const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
(acc, name) => {
// @ts-expect-error: name is a valid key, but TypeScript is dumb
acc[name] = (...args: any) => dispatch(actions[name](...args));
return acc;
},
{}
navigation.dispatch(
typeof payload === 'object' && payload != null
? { source: route.key, ...payload }
: payload
);
};
acc[route.key] = {
...rest,
...helpers,
...emitter.create(route.key),
dispatch,
setOptions: (options: object) =>
setOptions((o) => ({
...o,
[route.key]: { ...o[route.key], ...options },
})),
isFocused: () => {
const state = getState();
const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
(acc, name) => {
// @ts-expect-error: name is a valid key, but TypeScript is dumb
acc[name] = (...args: any) => dispatch(actions[name](...args));
return acc;
},
{}
);
if (state.routes[state.index].key !== route.key) {
return false;
}
acc[route.key] = {
...rest,
...helpers,
// FIXME: too much work to fix the types for now
...(emitter.create(route.key) as any),
dispatch,
setOptions: (options: object) =>
setOptions((o) => ({
...o,
[route.key]: { ...o[route.key], ...options },
})),
isFocused: () => {
const state = getState();
// If the current screen is focused, we also need to check if parent navigator is focused
// This makes sure that we return the focus state in the whole tree, not just this navigator
return navigation ? navigation.isFocused() : true;
},
};
}
if (state.routes[state.index].key !== route.key) {
return false;
}
return acc;
},
{}
);
// If the current screen is focused, we also need to check if parent navigator is focused
// This makes sure that we return the focus state in the whole tree, not just this navigator
return navigation ? navigation.isFocused() : true;
},
};
}
return acc;
}, {});
return cache.current;
}

View File

@@ -284,7 +284,7 @@ export type DrawerHeaderProps = {
/**
* Route object for the current screen.
*/
route: Route<string>;
route: RouteProp<ParamListBase, string>;
/**
* Navigation prop for the header.
*/
@@ -329,12 +329,9 @@ export type DrawerScreenProps<
};
export type DrawerDescriptor = Descriptor<
ParamListBase,
string,
DrawerNavigationState<ParamListBase>,
DrawerNavigationOptions
DrawerNavigationOptions,
DrawerNavigationProp<ParamListBase>,
RouteProp<ParamListBase, string>
>;
export type DrawerDescriptorMap = {
[key: string]: DrawerDescriptor;
};
export type DrawerDescriptorMap = Record<string, DrawerDescriptor>;

View File

@@ -83,15 +83,15 @@ export type MaterialBottomTabNavigationOptions = {
};
export type MaterialBottomTabDescriptor = Descriptor<
ParamListBase,
string,
TabNavigationState<ParamListBase>,
MaterialBottomTabNavigationOptions
MaterialBottomTabNavigationOptions,
MaterialBottomTabNavigationProp<ParamListBase>,
RouteProp<ParamListBase, string>
>;
export type MaterialBottomTabDescriptorMap = {
[key: string]: MaterialBottomTabDescriptor;
};
export type MaterialBottomTabDescriptorMap = Record<
string,
MaterialBottomTabDescriptor
>;
export type MaterialBottomTabNavigationConfig = Partial<
Omit<

View File

@@ -93,15 +93,15 @@ export type MaterialTopTabNavigationOptions = {
};
export type MaterialTopTabDescriptor = Descriptor<
ParamListBase,
string,
TabNavigationState<ParamListBase>,
MaterialTopTabNavigationOptions
MaterialTopTabNavigationOptions,
MaterialTopTabNavigationProp<ParamListBase>,
RouteProp<ParamListBase, string>
>;
export type MaterialTopTabDescriptorMap = {
[key: string]: MaterialTopTabDescriptor;
};
export type MaterialTopTabDescriptorMap = Record<
string,
MaterialTopTabDescriptor
>;
export type MaterialTopTabNavigationConfig = Partial<
Omit<

View File

@@ -238,7 +238,7 @@ export type StackHeaderProps = {
/**
* Route object for the current screen.
*/
route: Route<string>;
route: RouteProp<ParamListBase, string>;
/**
* Animated nodes representing the progress of the animation of the previous screen.
*/
@@ -267,15 +267,12 @@ export type StackHeaderProps = {
};
export type StackDescriptor = Descriptor<
ParamListBase,
string,
StackNavigationState<ParamListBase>,
StackNavigationOptions
StackNavigationOptions,
StackNavigationProp<ParamListBase>,
RouteProp<ParamListBase, string>
>;
export type StackDescriptorMap = {
[key: string]: StackDescriptor;
};
export type StackDescriptorMap = Record<string, StackDescriptor>;
export type StackNavigationOptions = StackHeaderOptions &
Partial<TransitionPreset> & {

View File

@@ -16,12 +16,12 @@ import HeaderContainer, {
Props as HeaderContainerProps,
} from '../Header/HeaderContainer';
import SafeAreaProviderCompat from '../SafeAreaProviderCompat';
import HeaderShownContext from '../../utils/HeaderShownContext';
import type {
StackNavigationHelpers,
StackNavigationConfig,
StackDescriptorMap,
} from '../../types';
import HeaderShownContext from '../../utils/HeaderShownContext';
type Props = StackNavigationConfig & {
state: StackNavigationState<ParamListBase>;