1 | import {
|
2 | getActionFromState,
|
3 | getStateFromPath,
|
4 | NavigationContainerRefContext,
|
5 | } from '@react-navigation/core';
|
6 | import * as React from 'react';
|
7 |
|
8 | import LinkingContext from './LinkingContext';
|
9 |
|
10 | export type To<
|
11 | ParamList extends ReactNavigation.RootParamList = ReactNavigation.RootParamList,
|
12 | RouteName extends keyof ParamList = keyof ParamList
|
13 | > =
|
14 | | string
|
15 | | (undefined extends ParamList[RouteName]
|
16 | ? {
|
17 | screen: Extract<RouteName, string>;
|
18 | params?: ParamList[RouteName];
|
19 | }
|
20 | : {
|
21 | screen: Extract<RouteName, string>;
|
22 | params: ParamList[RouteName];
|
23 | });
|
24 |
|
25 | export default function useLinkTo<
|
26 | ParamList extends ReactNavigation.RootParamList
|
27 | >() {
|
28 | const navigation = React.useContext(NavigationContainerRefContext);
|
29 | const linking = React.useContext(LinkingContext);
|
30 |
|
31 | const linkTo = React.useCallback(
|
32 | (to: To<ParamList>) => {
|
33 | if (navigation === undefined) {
|
34 | throw new Error(
|
35 | "Couldn't find a navigation object. Is your component inside NavigationContainer?"
|
36 | );
|
37 | }
|
38 |
|
39 | if (typeof to !== 'string') {
|
40 |
|
41 | navigation.navigate(to.screen, to.params);
|
42 | return;
|
43 | }
|
44 |
|
45 | if (!to.startsWith('/')) {
|
46 | throw new Error(`The path must start with '/' (${to}).`);
|
47 | }
|
48 |
|
49 | const { options } = linking;
|
50 |
|
51 | const state = options?.getStateFromPath
|
52 | ? options.getStateFromPath(to, options.config)
|
53 | : getStateFromPath(to, options?.config);
|
54 |
|
55 | if (state) {
|
56 | const action = getActionFromState(state, options?.config);
|
57 |
|
58 | if (action !== undefined) {
|
59 | navigation.dispatch(action);
|
60 | } else {
|
61 | navigation.reset(state);
|
62 | }
|
63 | } else {
|
64 | throw new Error('Failed to parse the path to a navigation state.');
|
65 | }
|
66 | },
|
67 | [linking, navigation]
|
68 | );
|
69 |
|
70 | return linkTo;
|
71 | }
|