UNPKG

3.88 kBJavaScriptView Raw
1import * as React from 'react';
2import NavigationBuilderContext from './NavigationBuilderContext';
3import useOnPreventRemove, { shouldPreventRemove } from './useOnPreventRemove';
4
5/**
6 * Hook to handle actions for a navigator, including state updates and bubbling.
7 *
8 * Bubbling an action is achieved in 2 ways:
9 * 1. To bubble action to parent, we expose the action handler in context and then access the parent context
10 * 2. To bubble action to child, child adds event listeners subscribing to actions from parent
11 *
12 * When the action handler handles as action, it returns `true`, otherwise `false`.
13 */
14export default function useOnAction(_ref) {
15 let {
16 router,
17 getState,
18 setState,
19 key,
20 actionListeners,
21 beforeRemoveListeners,
22 routerConfigOptions,
23 emitter
24 } = _ref;
25 const {
26 onAction: onActionParent,
27 onRouteFocus: onRouteFocusParent,
28 addListener: addListenerParent,
29 onDispatchAction
30 } = React.useContext(NavigationBuilderContext);
31 const routerConfigOptionsRef = React.useRef(routerConfigOptions);
32 React.useEffect(() => {
33 routerConfigOptionsRef.current = routerConfigOptions;
34 });
35 const onAction = React.useCallback(function (action) {
36 let visitedNavigators = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Set();
37 const state = getState(); // Since actions can bubble both up and down, they could come to the same navigator again
38 // We keep track of navigators which have already tried to handle the action and return if it's already visited
39
40 if (visitedNavigators.has(state.key)) {
41 return false;
42 }
43
44 visitedNavigators.add(state.key);
45
46 if (typeof action.target !== 'string' || action.target === state.key) {
47 let result = router.getStateForAction(state, action, routerConfigOptionsRef.current); // If a target is specified and set to current navigator, the action shouldn't bubble
48 // So instead of `null`, we use the state object for such cases to signal that action was handled
49
50 result = result === null && action.target === state.key ? state : result;
51
52 if (result !== null) {
53 onDispatchAction(action, state === result);
54
55 if (state !== result) {
56 const isPrevented = shouldPreventRemove(emitter, beforeRemoveListeners, state.routes, result.routes, action);
57
58 if (isPrevented) {
59 return true;
60 }
61
62 setState(result);
63 }
64
65 if (onRouteFocusParent !== undefined) {
66 // Some actions such as `NAVIGATE` also want to bring the navigated route to focus in the whole tree
67 // This means we need to focus all of the parent navigators of this navigator as well
68 const shouldFocus = router.shouldActionChangeFocus(action);
69
70 if (shouldFocus && key !== undefined) {
71 onRouteFocusParent(key);
72 }
73 }
74
75 return true;
76 }
77 }
78
79 if (onActionParent !== undefined) {
80 // Bubble action to the parent if the current navigator didn't handle it
81 if (onActionParent(action, visitedNavigators)) {
82 return true;
83 }
84 } // If the action wasn't handled by current navigator or a parent navigator, let children handle it
85
86
87 for (let i = actionListeners.length - 1; i >= 0; i--) {
88 const listener = actionListeners[i];
89
90 if (listener(action, visitedNavigators)) {
91 return true;
92 }
93 }
94
95 return false;
96 }, [actionListeners, beforeRemoveListeners, emitter, getState, key, onActionParent, onDispatchAction, onRouteFocusParent, router, setState]);
97 useOnPreventRemove({
98 getState,
99 emitter,
100 beforeRemoveListeners
101 });
102 React.useEffect(() => addListenerParent === null || addListenerParent === void 0 ? void 0 : addListenerParent('action', onAction), [addListenerParent, onAction]);
103 return onAction;
104}
105//# sourceMappingURL=useOnAction.js.map
\No newline at end of file