@react-navigation/core 8.0.0-alpha.3 → 8.0.0-alpha.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/BaseNavigationContainer.js +38 -19
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/ConsumedParamsContext.js +5 -0
- package/lib/module/ConsumedParamsContext.js.map +1 -0
- package/lib/module/DataLoading.js +127 -0
- package/lib/module/DataLoading.js.map +1 -0
- package/lib/module/NavigationBuilderContext.js +1 -0
- package/lib/module/NavigationBuilderContext.js.map +1 -1
- package/lib/module/NavigationIndependentTree.js +20 -8
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/NavigationProvider.js +22 -11
- package/lib/module/NavigationProvider.js.map +1 -1
- package/lib/module/NavigationStateContext.js +3 -0
- package/lib/module/NavigationStateContext.js.map +1 -1
- package/lib/module/PreventRemoveContext.js.map +1 -1
- package/lib/module/PreventRemoveProvider.js +37 -27
- package/lib/module/PreventRemoveProvider.js.map +1 -1
- package/lib/module/SceneView.js +10 -41
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/StaticNavigation.js +179 -50
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/StaticTreeContext.js +5 -0
- package/lib/module/StaticTreeContext.js.map +1 -0
- package/lib/module/checkSerializable.js +18 -13
- package/lib/module/checkSerializable.js.map +1 -1
- package/lib/module/createNavigationContainerRef.js +6 -8
- package/lib/module/createNavigationContainerRef.js.map +1 -1
- package/lib/module/createNavigatorFactory.js +27 -5
- package/lib/module/createNavigatorFactory.js.map +1 -1
- package/lib/module/findFocusedRoute.js +7 -4
- package/lib/module/findFocusedRoute.js.map +1 -1
- package/lib/module/getActionFromState.js +45 -20
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getPathFromState.js +171 -90
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getPatternParts.js +20 -4
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +487 -217
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/getStateFromRouteParams.js +23 -0
- package/lib/module/getStateFromRouteParams.js.map +1 -0
- package/lib/module/index.js +5 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/theming/useTheme.js +1 -1
- package/lib/module/theming/useTheme.js.map +1 -1
- package/lib/module/types.js +35 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDescriptors.js +21 -11
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useEventEmitter.js +48 -33
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useFocusEvents.js +12 -4
- package/lib/module/useFocusEvents.js.map +1 -1
- package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
- package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
- package/lib/module/useIsFocused.js +7 -12
- package/lib/module/useIsFocused.js.map +1 -1
- package/lib/module/useKeyedChildListeners.js +4 -2
- package/lib/module/useKeyedChildListeners.js.map +1 -1
- package/lib/module/useLazyValue.js.map +1 -1
- package/lib/module/useNavigation.js +2 -15
- package/lib/module/useNavigation.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +204 -131
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +22 -17
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +28 -10
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useNavigationIndependentTree.js +1 -1
- package/lib/module/useNavigationIndependentTree.js.map +1 -1
- package/lib/module/useNavigationState.js +125 -45
- package/lib/module/useNavigationState.js.map +1 -1
- package/lib/module/useOnAction.js +37 -7
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOnGetState.js +6 -3
- package/lib/module/useOnGetState.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +39 -30
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/module/useOnRouteFocus.js +1 -1
- package/lib/module/useOnRouteFocus.js.map +1 -1
- package/lib/module/useOptionsGetters.js +10 -5
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/usePreventRemove.js +10 -3
- package/lib/module/usePreventRemove.js.map +1 -1
- package/lib/module/usePreventRemoveContext.js +1 -1
- package/lib/module/usePreventRemoveContext.js.map +1 -1
- package/lib/module/useRegisterNavigator.js +2 -2
- package/lib/module/useRegisterNavigator.js.map +1 -1
- package/lib/module/useRoute.js +2 -0
- package/lib/module/useRoute.js.map +1 -1
- package/lib/module/useRouteCache.js +17 -11
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +9 -9
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/module/useStateForPath.js +1 -1
- package/lib/module/useStateForPath.js.map +1 -1
- package/lib/module/useSyncState.js +32 -9
- package/lib/module/useSyncState.js.map +1 -1
- package/lib/module/validatePathConfig.js +50 -36
- package/lib/module/validatePathConfig.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/ConsumedParamsContext.d.ts +3 -0
- package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
- package/lib/typescript/src/DataLoading.d.ts +21 -0
- package/lib/typescript/src/DataLoading.d.ts.map +1 -0
- package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
- package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
- package/lib/typescript/src/Group.d.ts +1 -1
- package/lib/typescript/src/Group.d.ts.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +9 -8
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
- package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
- package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
- package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
- package/lib/typescript/src/NavigationProvider.d.ts +12 -13
- package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
- package/lib/typescript/src/NavigationStateContext.d.ts +4 -3
- package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
- package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +4 -2
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/Screen.d.ts +1 -1
- package/lib/typescript/src/Screen.d.ts.map +1 -1
- package/lib/typescript/src/StaticContainer.d.ts +1 -1
- package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
- package/lib/typescript/src/StaticNavigation.d.ts +189 -109
- package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
- package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
- package/lib/typescript/src/checkSerializable.d.ts +8 -4
- package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
- package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
- package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/createNavigatorFactory.d.ts +23 -5
- package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
- package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
- package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
- package/lib/typescript/src/getActionFromState.d.ts +4 -4
- package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPathFromState.d.ts +3 -3
- package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPatternParts.d.ts +1 -0
- package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts +4 -7
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
- package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +36 -33
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
- package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
- package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
- package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/theming/useTheme.d.ts +1 -1
- package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +260 -159
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useChildListeners.d.ts +3 -3
- package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useComponent.d.ts +1 -1
- package/lib/typescript/src/useComponent.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts +10 -9
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useEventEmitter.d.ts +2 -2
- package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEvents.d.ts +2 -2
- package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
- package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
- package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
- package/lib/typescript/src/useIsFocused.d.ts +3 -0
- package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
- package/lib/typescript/src/useKeyedChildListeners.d.ts +3 -3
- package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
- package/lib/typescript/src/useNavigation.d.ts +4 -2
- package/lib/typescript/src/useNavigation.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +23 -30
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts +3 -3
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
- package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +19 -27
- package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationState.d.ts +17 -3
- package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
- package/lib/typescript/src/useOnAction.d.ts +9 -9
- package/lib/typescript/src/useOnAction.d.ts.map +1 -1
- package/lib/typescript/src/useOnGetState.d.ts +1 -1
- package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
- package/lib/typescript/src/useOnPreventRemove.d.ts +5 -9
- package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
- package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts +2 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/useRoute.d.ts +7 -3
- package/lib/typescript/src/useRoute.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts +5 -5
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
- package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/src/useStateForPath.d.ts +1 -1
- package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
- package/lib/typescript/src/useSyncState.d.ts +1 -0
- package/lib/typescript/src/useSyncState.d.ts.map +1 -1
- package/lib/typescript/src/utilities.d.ts +74 -37
- package/lib/typescript/src/utilities.d.ts.map +1 -1
- package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
- package/package.json +23 -22
- package/src/BaseNavigationContainer.tsx +362 -334
- package/src/ConsumedParamsContext.tsx +5 -0
- package/src/DataLoading.tsx +203 -0
- package/src/NavigationBuilderContext.tsx +14 -9
- package/src/NavigationFocusedRouteStateContext.tsx +4 -4
- package/src/NavigationIndependentTree.tsx +23 -12
- package/src/NavigationProvider.tsx +26 -15
- package/src/NavigationStateContext.tsx +9 -6
- package/src/PreventRemoveContext.tsx +1 -0
- package/src/PreventRemoveProvider.tsx +51 -46
- package/src/SceneView.tsx +11 -39
- package/src/StaticNavigation.tsx +659 -307
- package/src/StaticTreeContext.tsx +7 -0
- package/src/checkSerializable.tsx +35 -19
- package/src/createNavigationContainerRef.tsx +12 -9
- package/src/createNavigatorFactory.tsx +92 -8
- package/src/findFocusedRoute.tsx +16 -16
- package/src/getActionFromState.tsx +87 -51
- package/src/getPathFromState.tsx +254 -128
- package/src/getPatternParts.tsx +25 -4
- package/src/getStateFromPath.tsx +821 -331
- package/src/getStateFromRouteParams.tsx +59 -0
- package/src/index.tsx +55 -5
- package/src/theming/useTheme.tsx +1 -1
- package/src/types.tsx +798 -373
- package/src/useDescriptors.tsx +31 -14
- package/src/useEventEmitter.tsx +52 -38
- package/src/useFocusEvents.tsx +15 -7
- package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
- package/src/useIsFocused.tsx +14 -21
- package/src/useKeyedChildListeners.tsx +4 -2
- package/src/useLazyValue.tsx +2 -2
- package/src/useNavigation.tsx +7 -24
- package/src/useNavigationBuilder.tsx +314 -186
- package/src/useNavigationCache.tsx +32 -25
- package/src/useNavigationHelpers.tsx +27 -10
- package/src/useNavigationIndependentTree.tsx +1 -1
- package/src/useNavigationState.tsx +168 -69
- package/src/useOnAction.tsx +61 -13
- package/src/useOnGetState.tsx +5 -3
- package/src/useOnPreventRemove.tsx +75 -42
- package/src/useOnRouteFocus.tsx +10 -12
- package/src/useOptionsGetters.tsx +11 -5
- package/src/usePreventRemove.tsx +12 -3
- package/src/usePreventRemoveContext.tsx +1 -1
- package/src/useRegisterNavigator.tsx +2 -2
- package/src/useRoute.tsx +20 -6
- package/src/useRouteCache.tsx +22 -16
- package/src/useScheduleUpdate.tsx +9 -10
- package/src/useStateForPath.tsx +1 -1
- package/src/useSyncState.tsx +35 -9
- package/src/utilities.tsx +144 -31
- package/src/validatePathConfig.tsx +86 -57
- package/lib/module/CurrentRenderContext.js +0 -10
- package/lib/module/CurrentRenderContext.js.map +0 -1
- package/lib/module/useCurrentRender.js +0 -19
- package/lib/module/useCurrentRender.js.map +0 -1
- package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
- package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
- package/lib/typescript/src/useCurrentRender.d.ts +0 -14
- package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
- package/src/CurrentRenderContext.tsx +0 -9
- package/src/useCurrentRender.tsx +0 -31
|
@@ -5,10 +5,10 @@ import deepEqual from 'fast-deep-equal';
|
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { isValidElementType } from 'react-is';
|
|
7
7
|
import useLatestCallback from 'use-latest-callback';
|
|
8
|
+
import { ConsumedParamsContext } from "./ConsumedParamsContext.js";
|
|
8
9
|
import { deepFreeze } from "./deepFreeze.js";
|
|
9
10
|
import { Group } from "./Group.js";
|
|
10
11
|
import { isArrayEqual } from "./isArrayEqual.js";
|
|
11
|
-
import { isRecordEqual } from "./isRecordEqual.js";
|
|
12
12
|
import { NavigationBuilderContext } from "./NavigationBuilderContext.js";
|
|
13
13
|
import { NavigationHelpersContext } from "./NavigationHelpersContext.js";
|
|
14
14
|
import { NavigationMetaContext } from "./NavigationMetaContext.js";
|
|
@@ -20,11 +20,11 @@ import { PrivateValueStore } from "./types.js";
|
|
|
20
20
|
import { UnhandledActionContext } from "./UnhandledActionContext.js";
|
|
21
21
|
import { useChildListeners } from "./useChildListeners.js";
|
|
22
22
|
import { useComponent } from "./useComponent.js";
|
|
23
|
-
import { useCurrentRender } from "./useCurrentRender.js";
|
|
24
23
|
import { useDescriptors } from "./useDescriptors.js";
|
|
25
24
|
import { useEventEmitter } from "./useEventEmitter.js";
|
|
26
25
|
import { useFocusedListenersChildrenAdapter } from "./useFocusedListenersChildrenAdapter.js";
|
|
27
26
|
import { useFocusEvents } from "./useFocusEvents.js";
|
|
27
|
+
import { FocusedRouteKeyContext } from "./useIsFocused.js";
|
|
28
28
|
import { useKeyedChildListeners } from "./useKeyedChildListeners.js";
|
|
29
29
|
import { useLazyValue } from "./useLazyValue.js";
|
|
30
30
|
import { useNavigationHelpers } from "./useNavigationHelpers.js";
|
|
@@ -39,6 +39,7 @@ import { useScheduleUpdate } from "./useScheduleUpdate.js";
|
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
40
40
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
41
41
|
PrivateValueStore;
|
|
42
|
+
const isNavigationState = state => state != null && typeof state === 'object' && 'routes' in state && Array.isArray(state.routes);
|
|
42
43
|
const isScreen = child => {
|
|
43
44
|
return child.type === Screen;
|
|
44
45
|
};
|
|
@@ -52,7 +53,7 @@ const isValidKey = key => key === undefined || typeof key === 'string' && key !=
|
|
|
52
53
|
*
|
|
53
54
|
* @param children React Elements to extract the config from.
|
|
54
55
|
*/
|
|
55
|
-
const getRouteConfigsFromChildren = (children,
|
|
56
|
+
const getRouteConfigsFromChildren = (children, groupKeys = [], groupOptions, groupLayout) => {
|
|
56
57
|
const configs = React.Children.toArray(children).reduce((acc, child) => {
|
|
57
58
|
if (/*#__PURE__*/React.isValidElement(child)) {
|
|
58
59
|
if (isScreen(child)) {
|
|
@@ -69,7 +70,7 @@ const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayo
|
|
|
69
70
|
throw new Error(`Got an invalid 'navigationKey' prop (${JSON.stringify(child.props.navigationKey)}) for the screen '${child.props.name}'. It must be a non-empty string or 'undefined'.`);
|
|
70
71
|
}
|
|
71
72
|
acc.push({
|
|
72
|
-
keys: [
|
|
73
|
+
keys: [...groupKeys, child.props.navigationKey],
|
|
73
74
|
options: groupOptions,
|
|
74
75
|
layout: groupLayout,
|
|
75
76
|
props: child.props
|
|
@@ -83,14 +84,14 @@ const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayo
|
|
|
83
84
|
|
|
84
85
|
// When we encounter a fragment or group, we need to dive into its children to extract the configs
|
|
85
86
|
// This is handy to conditionally define a group of screens
|
|
86
|
-
acc.push(...getRouteConfigsFromChildren(child.props.children, child.props.navigationKey,
|
|
87
|
+
acc.push(...getRouteConfigsFromChildren(child.props.children, child.props.navigationKey != null ? [...groupKeys, child.props.navigationKey] : groupKeys,
|
|
87
88
|
// FIXME
|
|
88
89
|
// @ts-expect-error: add validation
|
|
89
90
|
child.type !== Group ? groupOptions : groupOptions != null ? [...groupOptions, child.props.screenOptions] : [child.props.screenOptions], typeof child.props.screenLayout === 'function' ? child.props.screenLayout : groupLayout));
|
|
90
91
|
return acc;
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
|
-
throw new Error(`A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found ${/*#__PURE__*/React.isValidElement(child) ? `'${typeof child.type === 'string' ? child.type : child.type?.name}'${child.props != null && typeof child.props === 'object' && 'name' in child.props && child.props?.name ? ` for the screen '${child.props.name}'` : ''}` : typeof child === 'object' ? JSON.stringify(child) : `'${String(child)}'`}). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`);
|
|
94
|
+
throw new Error(`A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found ${ /*#__PURE__*/React.isValidElement(child) ? `'${typeof child.type === 'string' ? child.type : child.type?.name}'${child.props != null && typeof child.props === 'object' && 'name' in child.props && child.props?.name ? ` for the screen '${child.props.name}'` : ''}` : typeof child === 'object' ? JSON.stringify(child) : `'${String(child)}'`}). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`);
|
|
94
95
|
}, []);
|
|
95
96
|
if (process.env.NODE_ENV !== 'production') {
|
|
96
97
|
configs.forEach(config => {
|
|
@@ -136,9 +137,16 @@ const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayo
|
|
|
136
137
|
}
|
|
137
138
|
return configs;
|
|
138
139
|
};
|
|
139
|
-
const getStateFromParams = params => {
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
const getStateFromParams = (params, type) => {
|
|
141
|
+
const state = params?.state;
|
|
142
|
+
if (isNavigationState(state)) {
|
|
143
|
+
if (state.type != null && state.type !== type) {
|
|
144
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
145
|
+
console.error(`The state passed in the params is for a navigator of type '${state.type}', but it was passed to a navigator of type '${type}'. This can happen if the state was meant for a different navigator. The state will be discarded.`);
|
|
146
|
+
}
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
return state;
|
|
142
150
|
} else if (typeof params?.screen === 'string' && params?.initial !== false) {
|
|
143
151
|
return {
|
|
144
152
|
routes: [{
|
|
@@ -160,7 +168,9 @@ const getStateFromParams = params => {
|
|
|
160
168
|
*/
|
|
161
169
|
export function useNavigationBuilder(createRouter, options) {
|
|
162
170
|
const navigatorKey = useRegisterNavigator();
|
|
163
|
-
const route = React.
|
|
171
|
+
const route = React.use(NavigationRouteContext);
|
|
172
|
+
const consumedParams = React.use(ConsumedParamsContext);
|
|
173
|
+
const isNestedParamsConsumed = typeof route?.params === 'object' && route.params != null ? consumedParams?.has(route.params) : false;
|
|
164
174
|
const {
|
|
165
175
|
children,
|
|
166
176
|
layout,
|
|
@@ -185,31 +195,26 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
185
195
|
}
|
|
186
196
|
return original;
|
|
187
197
|
});
|
|
188
|
-
const screens = routeConfigs.reduce((acc, config) => {
|
|
189
|
-
if (config.props.name in acc) {
|
|
190
|
-
throw new Error(`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`);
|
|
191
|
-
}
|
|
192
|
-
acc[config.props.name] = config;
|
|
193
|
-
return acc;
|
|
194
|
-
}, {});
|
|
195
198
|
const routeNames = routeConfigs.map(config => config.props.name);
|
|
196
|
-
const routeKeyList = routeNames.reduce((acc, curr) => {
|
|
197
|
-
acc[curr] = screens[curr].keys.map(key => key ?? '').join(':');
|
|
198
|
-
return acc;
|
|
199
|
-
}, {});
|
|
200
|
-
const routeParamList = routeNames.reduce((acc, curr) => {
|
|
201
|
-
const {
|
|
202
|
-
initialParams
|
|
203
|
-
} = screens[curr].props;
|
|
204
|
-
acc[curr] = initialParams;
|
|
205
|
-
return acc;
|
|
206
|
-
}, {});
|
|
207
|
-
const routeGetIdList = routeNames.reduce((acc, curr) => Object.assign(acc, {
|
|
208
|
-
[curr]: screens[curr].props.getId
|
|
209
|
-
}), {});
|
|
210
199
|
if (!routeNames.length) {
|
|
211
200
|
throw new Error("Couldn't find any screens for the navigator. Have you defined any screens as its children?");
|
|
212
201
|
}
|
|
202
|
+
const screens = {};
|
|
203
|
+
const routeKeyList = {};
|
|
204
|
+
const routeParamList = {};
|
|
205
|
+
const routeGetIdList = {};
|
|
206
|
+
for (const config of routeConfigs) {
|
|
207
|
+
const name = config.props.name;
|
|
208
|
+
if (name in screens) {
|
|
209
|
+
throw new Error(`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${name}')`);
|
|
210
|
+
}
|
|
211
|
+
screens[name] = config;
|
|
212
|
+
routeKeyList[name] = config.keys;
|
|
213
|
+
routeParamList[name] = config.props.initialParams;
|
|
214
|
+
Object.assign(routeGetIdList, {
|
|
215
|
+
[name]: config.props.getId
|
|
216
|
+
});
|
|
217
|
+
}
|
|
213
218
|
const isStateValid = React.useCallback(state => state.type === undefined || state.type === router.type, [router.type]);
|
|
214
219
|
const isStateInitialized = React.useCallback(state => state !== undefined && state.stale === false && isStateValid(state), [isStateValid]);
|
|
215
220
|
const doesStateHaveOnlyInvalidRoutes = React.useCallback(state => state.routes.every(r => !routeNames.includes(r.name)), [routeNames]);
|
|
@@ -217,25 +222,45 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
217
222
|
state: currentState,
|
|
218
223
|
getState: getCurrentState,
|
|
219
224
|
setState: setCurrentState,
|
|
225
|
+
subscribe,
|
|
220
226
|
setKey,
|
|
221
227
|
getKey,
|
|
222
228
|
getIsInitial
|
|
223
|
-
} = React.
|
|
224
|
-
const
|
|
229
|
+
} = React.use(NavigationStateContext);
|
|
230
|
+
const {
|
|
231
|
+
onEmitEvent,
|
|
232
|
+
getIsStateEmitted
|
|
233
|
+
} = React.use(NavigationBuilderContext);
|
|
234
|
+
const stateCleanupRef = React.useRef(false);
|
|
235
|
+
const lastStateRef = React.useRef(undefined);
|
|
225
236
|
const setState = useLatestCallback(state => {
|
|
226
|
-
if (
|
|
237
|
+
if (stateCleanupRef.current) {
|
|
238
|
+
// Store the state locally in case the current navigator is in `Activity`
|
|
239
|
+
lastStateRef.current = state;
|
|
240
|
+
|
|
227
241
|
// State might have been already cleaned up due to unmount
|
|
228
|
-
// We
|
|
229
|
-
//
|
|
242
|
+
// We don't want to update `route.state` in parent
|
|
243
|
+
// Otherwise it will be reused if a new navigator gets mounted
|
|
230
244
|
return;
|
|
231
245
|
}
|
|
232
246
|
setCurrentState(state);
|
|
233
247
|
});
|
|
234
|
-
const [stateBeforeInitialization, initializedState, isFirstStateInitialization] = React.useMemo(() => {
|
|
248
|
+
const [stateBeforeInitialization, initializedState, isFirstStateInitialization, paramsUsedForInitialization] = React.useMemo(() => {
|
|
249
|
+
// If the state was already cleaned up, but we have it stored in ref,
|
|
250
|
+
// It likely got cleaned up due to `<Activity mode="hidden">`
|
|
251
|
+
// We should reuse this state to avoid remounting screens
|
|
252
|
+
if (stateCleanupRef.current && lastStateRef.current && isStateValid(lastStateRef.current)) {
|
|
253
|
+
const state = isStateInitialized(lastStateRef.current) ? lastStateRef.current : router.getRehydratedState(lastStateRef.current, {
|
|
254
|
+
routeNames,
|
|
255
|
+
routeParamList,
|
|
256
|
+
routeGetIdList
|
|
257
|
+
});
|
|
258
|
+
return [undefined, state, false, undefined];
|
|
259
|
+
}
|
|
235
260
|
const initialRouteParamList = routeNames.reduce((acc, curr) => {
|
|
236
261
|
const {
|
|
237
262
|
initialParams
|
|
238
|
-
} = screens[curr]
|
|
263
|
+
} = screens[curr]?.props ?? {};
|
|
239
264
|
const initialParamsFromParams = route?.params?.state == null && route?.params?.initial !== false && route?.params?.screen === curr ? route.params.params : undefined;
|
|
240
265
|
acc[curr] = initialParams !== undefined || initialParamsFromParams !== undefined ? {
|
|
241
266
|
...initialParams,
|
|
@@ -248,24 +273,32 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
248
273
|
// We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
|
|
249
274
|
// Otherwise assume that the state was provided as initial state
|
|
250
275
|
// So we need to rehydrate it to make it usable
|
|
251
|
-
if ((currentState === undefined || !isStateValid(currentState)) && route?.params?.state == null && !(typeof route?.params?.screen === 'string' && route?.params?.initial !== false)) {
|
|
276
|
+
if ((currentState === undefined || !isStateValid(currentState)) && route?.params?.state == null && !(typeof route?.params?.screen === 'string' && route?.params?.initial !== false) && !isNestedParamsConsumed) {
|
|
252
277
|
return [undefined, router.getInitialState({
|
|
253
278
|
routeNames,
|
|
254
279
|
routeParamList: initialRouteParamList,
|
|
255
280
|
routeGetIdList
|
|
256
|
-
}), true];
|
|
281
|
+
}), true, undefined];
|
|
257
282
|
} else {
|
|
258
|
-
const
|
|
283
|
+
const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
|
|
284
|
+
const stateFromParams = paramsForState ? getStateFromParams(paramsForState, router.type) : undefined;
|
|
259
285
|
const stateBeforeInitialization = stateFromParams ?? currentState;
|
|
260
|
-
const hydratedState = router.
|
|
286
|
+
const hydratedState = stateBeforeInitialization == null ? router.getInitialState({
|
|
287
|
+
routeNames,
|
|
288
|
+
routeParamList: initialRouteParamList,
|
|
289
|
+
routeGetIdList
|
|
290
|
+
}) : router.getRehydratedState(stateBeforeInitialization, {
|
|
261
291
|
routeNames,
|
|
262
292
|
routeParamList: initialRouteParamList,
|
|
263
293
|
routeGetIdList
|
|
264
294
|
});
|
|
265
|
-
if (options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)) {
|
|
266
|
-
return [stateBeforeInitialization, hydratedState, true];
|
|
295
|
+
if (stateBeforeInitialization != null && isStateValid(stateBeforeInitialization) && options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)) {
|
|
296
|
+
return [stateBeforeInitialization, hydratedState, true, paramsForState];
|
|
267
297
|
}
|
|
268
|
-
return [undefined, hydratedState, false
|
|
298
|
+
return [undefined, hydratedState, false,
|
|
299
|
+
// Current state is already initialized
|
|
300
|
+
// So params weren't used, and need to be handled later
|
|
301
|
+
isStateInitialized(currentState) ? undefined : paramsForState];
|
|
269
302
|
}
|
|
270
303
|
// We explicitly don't include routeNames, route.params etc. in the dep list
|
|
271
304
|
// below. We want to avoid forcing a new state to be calculated in those cases
|
|
@@ -275,10 +308,17 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
275
308
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
276
309
|
}, [currentState, router, isStateValid]);
|
|
277
310
|
const previousRouteKeyListRef = React.useRef(routeKeyList);
|
|
278
|
-
React.
|
|
311
|
+
React.useInsertionEffect(() => {
|
|
279
312
|
previousRouteKeyListRef.current = routeKeyList;
|
|
280
313
|
});
|
|
281
314
|
const previousRouteKeyList = previousRouteKeyListRef.current;
|
|
315
|
+
|
|
316
|
+
// Names of the screens whose navigation key changed since the last render
|
|
317
|
+
const routeKeyChanges = Object.keys(routeKeyList).filter(name => {
|
|
318
|
+
const current = routeKeyList[name];
|
|
319
|
+
const previous = previousRouteKeyList[name];
|
|
320
|
+
return current != null && previous != null && !isArrayEqual(current, previous);
|
|
321
|
+
});
|
|
282
322
|
const [unhandledState, setUnhandledState] = React.useState(stateBeforeInitialization);
|
|
283
323
|
|
|
284
324
|
// An unhandled state is state that didn't have any valid routes
|
|
@@ -295,57 +335,65 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
295
335
|
isStateInitialized(currentState) ? currentState : initializedState;
|
|
296
336
|
let nextState = state;
|
|
297
337
|
let shouldClearUnhandledState = false;
|
|
298
|
-
|
|
338
|
+
if (unhandledState != null && !isStateValid(unhandledState)) {
|
|
339
|
+
// The stored unhandled state is for a different type of navigator
|
|
340
|
+
// It can't be used with this navigator, so we discard it
|
|
341
|
+
setUnhandledState(undefined);
|
|
342
|
+
} else if (
|
|
299
343
|
// Previously unhandled state is now valid again
|
|
300
344
|
// And current state no longer has any valid routes
|
|
301
345
|
// We should reuse the unhandled state instead of re-calculating the state
|
|
302
|
-
|
|
346
|
+
unhandledState?.routes.every(r => routeNames.includes(r.name)) && state?.routes.every(r => !routeNames.includes(r.name))) {
|
|
303
347
|
shouldClearUnhandledState = true;
|
|
304
348
|
nextState = router.getRehydratedState(unhandledState, {
|
|
305
349
|
routeNames,
|
|
306
350
|
routeParamList,
|
|
307
351
|
routeGetIdList
|
|
308
352
|
});
|
|
309
|
-
} else if (!isArrayEqual(state.routeNames, routeNames) ||
|
|
353
|
+
} else if (!isArrayEqual(state.routeNames, routeNames) || routeKeyChanges.length !== 0) {
|
|
310
354
|
// When the list of route names change, the router should handle it to remove invalid routes
|
|
311
355
|
nextState = router.getStateForRouteNamesChange(state, {
|
|
312
356
|
routeNames,
|
|
313
357
|
routeParamList,
|
|
314
358
|
routeGetIdList,
|
|
315
|
-
routeKeyChanges
|
|
359
|
+
routeKeyChanges
|
|
316
360
|
});
|
|
317
361
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
previousNestedParamsRef.current = route?.params;
|
|
321
|
-
}, [route?.params]);
|
|
322
|
-
if (route?.params) {
|
|
323
|
-
const previousParams = previousNestedParamsRef.current;
|
|
362
|
+
let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
|
|
363
|
+
if (route?.params && !didConsumeNestedParams) {
|
|
324
364
|
let action;
|
|
325
|
-
if (
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
365
|
+
if (isNavigationState(route.params.state) && !isNestedParamsConsumed) {
|
|
366
|
+
didConsumeNestedParams = true;
|
|
367
|
+
const stateFromParams = getStateFromParams(route.params, router.type);
|
|
368
|
+
if (stateFromParams != null) {
|
|
369
|
+
if (options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(stateFromParams)) {
|
|
370
|
+
if (stateFromParams !== unhandledState) {
|
|
371
|
+
setUnhandledState(stateFromParams);
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
// If the route was updated with new state, we should reset to it
|
|
375
|
+
action = CommonActions.reset(stateFromParams);
|
|
329
376
|
}
|
|
330
|
-
} else {
|
|
331
|
-
// If the route was updated with new state, we should reset to it
|
|
332
|
-
action = CommonActions.reset(route.params.state);
|
|
333
377
|
}
|
|
334
|
-
} else if (typeof route.params.screen === 'string' && (route.params.initial === false && isFirstStateInitialization ||
|
|
378
|
+
} else if (typeof route.params.screen === 'string' && (route.params.initial === false && isFirstStateInitialization || !isNestedParamsConsumed)) {
|
|
379
|
+
didConsumeNestedParams = true;
|
|
335
380
|
if (options.routeNamesChangeBehavior === 'lastUnhandled' && !routeNames.includes(route.params.screen)) {
|
|
336
|
-
const state = getStateFromParams(route.params);
|
|
381
|
+
const state = getStateFromParams(route.params, router.type);
|
|
337
382
|
if (state != null && !deepEqual(state, unhandledState)) {
|
|
338
383
|
setUnhandledState(state);
|
|
339
384
|
}
|
|
340
385
|
} else {
|
|
341
386
|
// If the route was updated with new screen name and/or params, we should navigate there
|
|
342
|
-
action =
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
387
|
+
action = {
|
|
388
|
+
type: 'NAVIGATE',
|
|
389
|
+
payload: {
|
|
390
|
+
name: route.params.screen,
|
|
391
|
+
params: route.params.params,
|
|
392
|
+
path: route.params.path,
|
|
393
|
+
merge: route.params.merge,
|
|
394
|
+
pop: route.params.pop
|
|
395
|
+
}
|
|
396
|
+
};
|
|
349
397
|
}
|
|
350
398
|
}
|
|
351
399
|
|
|
@@ -355,16 +403,25 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
355
403
|
routeParamList,
|
|
356
404
|
routeGetIdList
|
|
357
405
|
}) : null;
|
|
406
|
+
if (process.env.NODE_ENV !== 'production' && action != null && updatedState === null) {
|
|
407
|
+
const names = routeNames.map(name => `'${name}'`).join(', ');
|
|
408
|
+
console.error(`The 'state' or 'screen' passed in the params couldn't be applied to the navigator with the screens: ${names}. This can happen if the params contain names of screens that don't exist in the navigator. The navigation specified in the params will be ignored.`);
|
|
409
|
+
}
|
|
358
410
|
nextState = updatedState !== null ? router.getRehydratedState(updatedState, {
|
|
359
411
|
routeNames,
|
|
360
412
|
routeParamList,
|
|
361
413
|
routeGetIdList
|
|
362
414
|
}) : nextState;
|
|
363
415
|
}
|
|
364
|
-
|
|
416
|
+
React.useEffect(() => {
|
|
417
|
+
if (consumedParams && didConsumeNestedParams && typeof route?.params === 'object' && route.params != null) {
|
|
418
|
+
consumedParams.set(route.params, true);
|
|
419
|
+
}
|
|
420
|
+
}, [consumedParams, didConsumeNestedParams, route?.params]);
|
|
421
|
+
const shouldUpdate = state !== nextState;
|
|
365
422
|
useScheduleUpdate(() => {
|
|
366
423
|
if (shouldUpdate) {
|
|
367
|
-
//
|
|
424
|
+
// Schedule an update if the state needs to be updated
|
|
368
425
|
setState(nextState);
|
|
369
426
|
if (shouldClearUnhandledState) {
|
|
370
427
|
setUnhandledState(undefined);
|
|
@@ -376,44 +433,54 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
376
433
|
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
|
|
377
434
|
// So we override the state object we return to use the latest state as soon as possible
|
|
378
435
|
state = nextState;
|
|
436
|
+
|
|
437
|
+
// Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
|
|
438
|
+
React.useEffect(() => {
|
|
439
|
+
lastStateRef.current = state;
|
|
440
|
+
});
|
|
441
|
+
const lastNotifiedStateRef = React.useRef(null);
|
|
379
442
|
React.useEffect(() => {
|
|
380
443
|
// In strict mode, React will double-invoke effects.
|
|
381
444
|
// So we need to reset the flag if component was not unmounted
|
|
382
|
-
|
|
445
|
+
stateCleanupRef.current = false;
|
|
383
446
|
setKey(navigatorKey);
|
|
384
|
-
if (!getIsInitial()) {
|
|
385
|
-
//
|
|
386
|
-
//
|
|
447
|
+
if ((!getIsInitial() || getIsStateEmitted()) && lastNotifiedStateRef.current !== state) {
|
|
448
|
+
// We need to notify the state update in these scenarios:
|
|
449
|
+
// 1. If it's not the initial render of the component containing the navigator
|
|
450
|
+
// 2. If the container has already emitted state before the navigator mounted
|
|
451
|
+
//
|
|
452
|
+
// This will make sure that the container gets notified of state changes due to new mounts
|
|
387
453
|
// This is necessary for proper screen tracking, URL updates etc.
|
|
388
|
-
|
|
454
|
+
//
|
|
455
|
+
// During hydration, a nested navigator may mount after the container's initial state event.
|
|
456
|
+
// Or mount could be delayed due to conditional rendering, suspense etc.
|
|
457
|
+
// So only checking for the initial render is not enough.
|
|
458
|
+
// We also need to propagate the navigator's initial state when it mounts later,
|
|
459
|
+
// so the container is notified that the nested state is now available.
|
|
460
|
+
//
|
|
461
|
+
// We only notify if the state is different from what we already notified
|
|
462
|
+
// Otherwise, this goes into a loop when inside `<Activity mode="hidden">`
|
|
463
|
+
setState(state);
|
|
464
|
+
lastNotifiedStateRef.current = state;
|
|
389
465
|
}
|
|
390
466
|
return () => {
|
|
391
467
|
// We need to clean up state for this navigator on unmount
|
|
392
468
|
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
|
393
469
|
setCurrentState(undefined);
|
|
394
|
-
|
|
470
|
+
stateCleanupRef.current = true;
|
|
395
471
|
}
|
|
472
|
+
|
|
473
|
+
// Reset so that StrictMode's second mount re-propagates state correctly.
|
|
474
|
+
// Without this, the guard above sees the same reference and skips setState,
|
|
475
|
+
// causing the initial state to be lost after cleanup wipes the container state.
|
|
476
|
+
lastNotifiedStateRef.current = null;
|
|
396
477
|
};
|
|
397
478
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
398
479
|
}, []);
|
|
399
|
-
|
|
400
|
-
// In some cases (e.g. route names change), internal state might have changed
|
|
401
|
-
// But it hasn't been committed yet, so hasn't propagated to the sync external store
|
|
402
|
-
// During this time, we need to return the internal state in `getState`
|
|
403
|
-
// Otherwise it can result in inconsistent state during render in children
|
|
404
|
-
// To avoid this, we use a ref for render phase, and immediately clear it on commit
|
|
405
|
-
const stateRef = React.useRef(state);
|
|
406
|
-
stateRef.current = state;
|
|
407
|
-
React.useLayoutEffect(() => {
|
|
408
|
-
stateRef.current = null;
|
|
409
|
-
});
|
|
410
480
|
const getState = useLatestCallback(() => {
|
|
411
481
|
const currentState = getCurrentState();
|
|
412
482
|
return deepFreeze(isStateInitialized(currentState) ? currentState : initializedState);
|
|
413
483
|
});
|
|
414
|
-
const {
|
|
415
|
-
onEmitEvent
|
|
416
|
-
} = React.useContext(NavigationBuilderContext);
|
|
417
484
|
const emitter = useEventEmitter(e => {
|
|
418
485
|
const routeNames = [];
|
|
419
486
|
let route;
|
|
@@ -429,32 +496,33 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
429
496
|
if (route == null) {
|
|
430
497
|
return;
|
|
431
498
|
}
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
499
|
+
const hasPerScreenListeners = routeNames.some(name => screens[name]?.props.listeners != null);
|
|
500
|
+
if (screenListeners != null || hasPerScreenListeners) {
|
|
501
|
+
const descriptor = descriptors[route.key];
|
|
502
|
+
if (descriptor == null) {
|
|
503
|
+
throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
|
|
504
|
+
}
|
|
505
|
+
const navigation = descriptor.navigation;
|
|
506
|
+
const listeners = [].concat(
|
|
507
|
+
// Get an array of listeners for all screens + common listeners on navigator
|
|
508
|
+
...[screenListeners, ...routeNames.map(name => {
|
|
509
|
+
const {
|
|
510
|
+
listeners
|
|
511
|
+
} = screens[name]?.props ?? {};
|
|
512
|
+
return listeners;
|
|
513
|
+
})].map(listeners => {
|
|
514
|
+
const map = typeof listeners === 'function' ? listeners({
|
|
515
|
+
route: route,
|
|
516
|
+
navigation
|
|
517
|
+
}) : listeners;
|
|
518
|
+
return map ? Object.keys(map).filter(type => type === e.type).map(type => map?.[type]) : undefined;
|
|
519
|
+
}))
|
|
520
|
+
// We don't want same listener to be called multiple times for same event
|
|
521
|
+
// So we remove any duplicate functions from the array
|
|
522
|
+
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
|
523
|
+
listeners.forEach(listener => listener?.(e));
|
|
524
|
+
}
|
|
525
|
+
}, onEmitEvent);
|
|
458
526
|
useFocusEvents({
|
|
459
527
|
state,
|
|
460
528
|
emitter
|
|
@@ -495,7 +563,7 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
495
563
|
getState,
|
|
496
564
|
setState
|
|
497
565
|
});
|
|
498
|
-
const onUnhandledActionParent = React.
|
|
566
|
+
const onUnhandledActionParent = React.use(UnhandledActionContext);
|
|
499
567
|
const onUnhandledAction = useLatestCallback(action => {
|
|
500
568
|
if (options.routeNamesChangeBehavior === 'lastUnhandled' && action.type === 'NAVIGATE' && action.payload != null && 'name' in action.payload && typeof action.payload.name === 'string' && !routeNames.includes(action.payload.name)) {
|
|
501
569
|
const state = {
|
|
@@ -513,9 +581,9 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
513
581
|
onAction,
|
|
514
582
|
onUnhandledAction,
|
|
515
583
|
getState,
|
|
584
|
+
state,
|
|
516
585
|
emitter,
|
|
517
|
-
router
|
|
518
|
-
stateRef
|
|
586
|
+
router
|
|
519
587
|
});
|
|
520
588
|
useFocusedListenersChildrenAdapter({
|
|
521
589
|
navigation,
|
|
@@ -526,7 +594,7 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
526
594
|
getStateListeners: keyedListeners.getState
|
|
527
595
|
});
|
|
528
596
|
const descriptors = useDescriptors({
|
|
529
|
-
routes:
|
|
597
|
+
routes: state.routes,
|
|
530
598
|
screens,
|
|
531
599
|
navigation,
|
|
532
600
|
screenOptions,
|
|
@@ -534,6 +602,7 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
534
602
|
onAction,
|
|
535
603
|
getState,
|
|
536
604
|
setState,
|
|
605
|
+
subscribe,
|
|
537
606
|
onRouteFocus,
|
|
538
607
|
addListener,
|
|
539
608
|
addKeyedListener,
|
|
@@ -541,12 +610,11 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
541
610
|
// @ts-expect-error: this should have both core and custom events, but too much work right now
|
|
542
611
|
emitter
|
|
543
612
|
});
|
|
544
|
-
useCurrentRender({
|
|
545
|
-
state,
|
|
546
|
-
navigation,
|
|
547
|
-
descriptors
|
|
548
|
-
});
|
|
549
613
|
const NavigationContent = useComponent(children => {
|
|
614
|
+
const focusedRoute = state.routes[state.index];
|
|
615
|
+
if (focusedRoute == null) {
|
|
616
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
617
|
+
}
|
|
550
618
|
const element = layout != null ? layout({
|
|
551
619
|
state,
|
|
552
620
|
descriptors,
|
|
@@ -559,8 +627,13 @@ export function useNavigationBuilder(createRouter, options) {
|
|
|
559
627
|
value: navigation,
|
|
560
628
|
children: /*#__PURE__*/_jsx(NavigationStateListenerProvider, {
|
|
561
629
|
state: state,
|
|
562
|
-
|
|
563
|
-
|
|
630
|
+
getState: navigation.getState,
|
|
631
|
+
subscribe: subscribe,
|
|
632
|
+
children: /*#__PURE__*/_jsx(FocusedRouteKeyContext.Provider, {
|
|
633
|
+
value: focusedRoute.key,
|
|
634
|
+
children: /*#__PURE__*/_jsx(PreventRemoveProvider, {
|
|
635
|
+
children: element
|
|
636
|
+
})
|
|
564
637
|
})
|
|
565
638
|
})
|
|
566
639
|
})
|