xref: /expo/packages/expo-router/src/hooks.ts (revision a51fdfe6)
1import { NavigationRouteContext, ParamListBase, RouteProp } from '@react-navigation/native';
2import React from 'react';
3
4import { store, useStoreRootState, useStoreRouteInfo } from './global-state/router-store';
5import { Router } from './types';
6
7type SearchParams = Record<string, string | string[]>;
8
9export function useRootNavigationState() {
10  return useStoreRootState();
11}
12
13export function useRouteInfo() {
14  return useStoreRouteInfo();
15}
16
17export function useRootNavigation() {
18  return store.navigationRef.current;
19}
20
21export function useRouter(): Router {
22  return React.useMemo(
23    () => ({
24      push: store.push,
25      back: store.goBack,
26      replace: store.replace,
27      setParams: store.setParams,
28      canGoBack: store.canGoBack,
29      // TODO(EvanBacon): add `reload`
30    }),
31    []
32  );
33}
34
35/**
36 * @private
37 * @returns the current global pathname with query params attached. This may change in the future to include the hostname from a predefined universal link, i.e. `/foobar?hey=world` becomes `https://acme.dev/foobar?hey=world`
38 */
39export function useUnstableGlobalHref(): string {
40  return useStoreRouteInfo().unstable_globalHref;
41}
42
43/**
44 * Get a list of selected file segments for the currently selected route. Segments are not normalized, so they will be the same as the file path. e.g. /[id]?id=normal -> ["[id]"]
45 *
46 * `useSegments` can be typed using an abstract.
47 * Consider the following file structure, and strictly typed `useSegments` function:
48 *
49 * ```md
50 * - app
51 *   - [user]
52 *     - index.js
53 *     - followers.js
54 *   - settings.js
55 * ```
56 * This can be strictly typed using the following abstract:
57 *
58 * ```ts
59 * const [first, second] = useSegments<['settings'] | ['[user]'] | ['[user]', 'followers']>()
60 * ```
61 */
62export function useSegments<TSegments extends string[] = string[]>(): TSegments {
63  return useStoreRouteInfo().segments as TSegments;
64}
65
66/** @returns global selected pathname without query parameters. */
67export function usePathname(): string {
68  return useStoreRouteInfo().pathname;
69}
70
71/**
72 * Get the globally selected query parameters, including dynamic path segments. This function will update even when the route is not focused.
73 * Useful for analytics or other background operations that don't draw to the screen.
74 *
75 * When querying search params in a stack, opt-towards using `useLocalSearchParams` as these will only
76 * update when the route is focused.
77 *
78 * @see `useLocalSearchParams`
79 */
80export function useGlobalSearchParams<
81  TParams extends SearchParams = SearchParams,
82>(): Partial<TParams> {
83  return useStoreRouteInfo().params as Partial<TParams>;
84}
85
86/**
87 * Returns the URL search parameters for the contextually focused route. e.g. `/acme?foo=bar` -> `{ foo: "bar" }`.
88 * This is useful for stacks where you may push a new screen that changes the query parameters.
89 *
90 * To observe updates even when the invoking route is not focused, use `useGlobalSearchParams()`.
91 */
92export function useLocalSearchParams<
93  TParams extends SearchParams = SearchParams,
94>(): Partial<TParams> {
95  return (useOptionalLocalRoute()?.params ?? ({} as any)) as Partial<TParams>;
96}
97
98function useOptionalLocalRoute<T extends RouteProp<ParamListBase>>(): T | undefined {
99  const route = React.useContext(NavigationRouteContext);
100  return route as T | undefined;
101}
102