UNPKG

3.89 kBJavaScriptView Raw
1import { CommonActions } from '@react-navigation/routers';
2import * as React from 'react';
3import NavigationBuilderContext from './NavigationBuilderContext';
4
5/**
6 * Hook to cache navigation objects for each screen in the navigator.
7 * It's important to cache them to make sure navigation objects don't change between renders.
8 * This lets us apply optimizations like `React.memo` to minimize re-rendering screens.
9 */
10export default function useNavigationCache(_ref) {
11 let {
12 state,
13 getState,
14 navigation,
15 setOptions,
16 router,
17 emitter
18 } = _ref;
19 const {
20 stackRef
21 } = React.useContext(NavigationBuilderContext); // Cache object which holds navigation objects for each screen
22 // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
23 // In reality, these deps will rarely change, if ever
24
25 const cache = React.useMemo(() => ({
26 current: {}
27 }), // eslint-disable-next-line react-hooks/exhaustive-deps
28 [getState, navigation, setOptions, router, emitter]);
29 const actions = { ...router.actionCreators,
30 ...CommonActions
31 };
32 cache.current = state.routes.reduce((acc, route) => {
33 const previous = cache.current[route.key];
34
35 if (previous) {
36 // If a cached navigation object already exists, reuse it
37 acc[route.key] = previous;
38 } else {
39 // eslint-disable-next-line @typescript-eslint/no-unused-vars
40 const {
41 emit,
42 ...rest
43 } = navigation;
44
45 const dispatch = thunk => {
46 const action = typeof thunk === 'function' ? thunk(getState()) : thunk;
47
48 if (action != null) {
49 navigation.dispatch({
50 source: route.key,
51 ...action
52 });
53 }
54 };
55
56 const withStack = callback => {
57 let isStackSet = false;
58
59 try {
60 if (process.env.NODE_ENV !== 'production' && stackRef && !stackRef.current) {
61 // Capture the stack trace for devtools
62 stackRef.current = new Error().stack;
63 isStackSet = true;
64 }
65
66 callback();
67 } finally {
68 if (isStackSet && stackRef) {
69 stackRef.current = undefined;
70 }
71 }
72 };
73
74 const helpers = Object.keys(actions).reduce((acc, name) => {
75 acc[name] = function () {
76 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
77 args[_key] = arguments[_key];
78 }
79
80 return withStack(() => // @ts-expect-error: name is a valid key, but TypeScript is dumb
81 dispatch(actions[name](...args)));
82 };
83
84 return acc;
85 }, {});
86 acc[route.key] = { ...rest,
87 ...helpers,
88 // FIXME: too much work to fix the types for now
89 ...emitter.create(route.key),
90 dispatch: thunk => withStack(() => dispatch(thunk)),
91 getParent: id => {
92 if (id !== undefined && id === rest.getId()) {
93 // If the passed id is the same as the current navigation id,
94 // we return the cached navigation object for the relevant route
95 return acc[route.key];
96 }
97
98 return rest.getParent(id);
99 },
100 setOptions: options => setOptions(o => ({ ...o,
101 [route.key]: { ...o[route.key],
102 ...options
103 }
104 })),
105 isFocused: () => {
106 const state = getState();
107
108 if (state.routes[state.index].key !== route.key) {
109 return false;
110 } // If the current screen is focused, we also need to check if parent navigator is focused
111 // This makes sure that we return the focus state in the whole tree, not just this navigator
112
113
114 return navigation ? navigation.isFocused() : true;
115 }
116 };
117 }
118
119 return acc;
120 }, {});
121 return cache.current;
122}
123//# sourceMappingURL=useNavigationCache.js.map
\No newline at end of file