import * as React from 'react'; import type { NavigationAction, NavigationState, ParamListBase, Router, } from '@react-navigation/routers'; import SceneView from './SceneView'; import NavigationBuilderContext, { AddListener, AddKeyedListener, } from './NavigationBuilderContext'; import type { NavigationEventEmitter } from './useEventEmitter'; import useNavigationCache from './useNavigationCache'; import useRouteCache from './useRouteCache'; import NavigationContext from './NavigationContext'; import NavigationRouteContext from './NavigationRouteContext'; import type { Descriptor, NavigationHelpers, RouteConfig, RouteProp, EventMapBase, } from './types'; type Options< State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase > = { state: State; screens: Record< string, RouteConfig >; navigation: NavigationHelpers; screenOptions?: | ScreenOptions | ((props: { route: RouteProp; navigation: any; }) => ScreenOptions); onAction: ( action: NavigationAction, visitedNavigators?: Set ) => boolean; getState: () => State; setState: (state: State) => void; addListener: AddListener; addKeyedListener: AddKeyedListener; onRouteFocus: (key: string) => void; router: Router; emitter: NavigationEventEmitter; }; /** * Hook to create descriptor objects for the child routes. * * A descriptor object provides 3 things: * - Helper method to render a screen * - Options specified by the screen for the navigator * - Navigation object intended for the route */ export default function useDescriptors< State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase >({ state, screens, navigation, screenOptions, onAction, getState, setState, addListener, addKeyedListener, onRouteFocus, router, emitter, }: Options) { const [options, setOptions] = React.useState>({}); const { onDispatchAction, onOptionsChange } = React.useContext( NavigationBuilderContext ); const context = React.useMemo( () => ({ navigation, onAction, addListener, addKeyedListener, onRouteFocus, onDispatchAction, onOptionsChange, }), [ navigation, onAction, addListener, addKeyedListener, onRouteFocus, onDispatchAction, onOptionsChange, ] ); const navigations = useNavigationCache({ state, getState, navigation, setOptions, router, emitter, }); const routes = useRouteCache(state.routes); return routes.reduce< Record> >((acc, route, i) => { const screen = screens[route.name]; const navigation = navigations[route.key]; const routeOptions = { // The default `screenOptions` passed to the navigator ...(typeof screenOptions === 'object' || screenOptions == null ? screenOptions : // @ts-expect-error: this is a function, but typescript doesn't think so screenOptions({ route, navigation, })), // The `options` prop passed to `Screen` elements ...(typeof screen.options === 'object' || screen.options == null ? screen.options : // @ts-expect-error: this is a function, but typescript doesn't think so screen.options({ route, navigation, })), // The options set via `navigation.setOptions` ...options[route.key], }; acc[route.key] = { navigation, render() { return ( ); }, options: routeOptions as ScreenOptions, }; return acc; }, {}); }