mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-12 22:51:18 +08:00
fix: add missing helper types in descriptors
This commit is contained in:
@@ -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> = {
|
||||
/**
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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>
|
||||
>
|
||||
>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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> & {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user