@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
|
@@ -15,10 +15,10 @@ import * as React from 'react';
|
|
|
15
15
|
import { isValidElementType } from 'react-is';
|
|
16
16
|
import useLatestCallback from 'use-latest-callback';
|
|
17
17
|
|
|
18
|
+
import { ConsumedParamsContext } from './ConsumedParamsContext';
|
|
18
19
|
import { deepFreeze } from './deepFreeze';
|
|
19
20
|
import { Group } from './Group';
|
|
20
21
|
import { isArrayEqual } from './isArrayEqual';
|
|
21
|
-
import { isRecordEqual } from './isRecordEqual';
|
|
22
22
|
import { NavigationBuilderContext } from './NavigationBuilderContext';
|
|
23
23
|
import { NavigationHelpersContext } from './NavigationHelpersContext';
|
|
24
24
|
import { NavigationMetaContext } from './NavigationMetaContext';
|
|
@@ -37,11 +37,11 @@ import {
|
|
|
37
37
|
import { UnhandledActionContext } from './UnhandledActionContext';
|
|
38
38
|
import { useChildListeners } from './useChildListeners';
|
|
39
39
|
import { useComponent } from './useComponent';
|
|
40
|
-
import { useCurrentRender } from './useCurrentRender';
|
|
41
40
|
import { type ScreenConfigWithParent, useDescriptors } from './useDescriptors';
|
|
42
41
|
import { useEventEmitter } from './useEventEmitter';
|
|
43
42
|
import { useFocusedListenersChildrenAdapter } from './useFocusedListenersChildrenAdapter';
|
|
44
43
|
import { useFocusEvents } from './useFocusEvents';
|
|
44
|
+
import { FocusedRouteKeyContext } from './useIsFocused';
|
|
45
45
|
import { useKeyedChildListeners } from './useKeyedChildListeners';
|
|
46
46
|
import { useLazyValue } from './useLazyValue';
|
|
47
47
|
import { useNavigationHelpers } from './useNavigationHelpers';
|
|
@@ -58,9 +58,17 @@ PrivateValueStore;
|
|
|
58
58
|
|
|
59
59
|
type NavigatorRoute = {
|
|
60
60
|
key: string;
|
|
61
|
-
params?: NavigatorScreenParams<ParamListBase
|
|
61
|
+
params?: NavigatorScreenParams<ParamListBase> | undefined;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
const isNavigationState = (
|
|
65
|
+
state: unknown
|
|
66
|
+
): state is NavigationState | PartialState<NavigationState> =>
|
|
67
|
+
state != null &&
|
|
68
|
+
typeof state === 'object' &&
|
|
69
|
+
'routes' in state &&
|
|
70
|
+
Array.isArray(state.routes);
|
|
71
|
+
|
|
64
72
|
const isScreen = (
|
|
65
73
|
child: React.ReactElement<unknown>
|
|
66
74
|
): child is React.ReactElement<{
|
|
@@ -95,7 +103,7 @@ const getRouteConfigsFromChildren = <
|
|
|
95
103
|
EventMap extends EventMapBase,
|
|
96
104
|
>(
|
|
97
105
|
children: React.ReactNode,
|
|
98
|
-
|
|
106
|
+
groupKeys: string[] = [],
|
|
99
107
|
groupOptions?: ScreenConfigWithParent<
|
|
100
108
|
State,
|
|
101
109
|
ScreenOptions,
|
|
@@ -138,7 +146,7 @@ const getRouteConfigsFromChildren = <
|
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
acc.push({
|
|
141
|
-
keys: [
|
|
149
|
+
keys: [...groupKeys, child.props.navigationKey],
|
|
142
150
|
options: groupOptions,
|
|
143
151
|
layout: groupLayout,
|
|
144
152
|
props: child.props as RouteConfig<
|
|
@@ -168,7 +176,9 @@ const getRouteConfigsFromChildren = <
|
|
|
168
176
|
acc.push(
|
|
169
177
|
...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
|
|
170
178
|
child.props.children as React.ReactNode,
|
|
171
|
-
child.props.navigationKey
|
|
179
|
+
child.props.navigationKey != null
|
|
180
|
+
? [...groupKeys, child.props.navigationKey]
|
|
181
|
+
: groupKeys,
|
|
172
182
|
// FIXME
|
|
173
183
|
// @ts-expect-error: add validation
|
|
174
184
|
child.type !== Group
|
|
@@ -276,9 +286,24 @@ const getRouteConfigsFromChildren = <
|
|
|
276
286
|
return configs;
|
|
277
287
|
};
|
|
278
288
|
|
|
279
|
-
const getStateFromParams = (
|
|
280
|
-
|
|
281
|
-
|
|
289
|
+
const getStateFromParams = (
|
|
290
|
+
params: NavigatorRoute['params'],
|
|
291
|
+
type: string
|
|
292
|
+
): NavigationState | PartialState<NavigationState> | undefined => {
|
|
293
|
+
const state = params?.state;
|
|
294
|
+
|
|
295
|
+
if (isNavigationState(state)) {
|
|
296
|
+
if (state.type != null && state.type !== type) {
|
|
297
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
298
|
+
console.error(
|
|
299
|
+
`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.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return state;
|
|
282
307
|
} else if (typeof params?.screen === 'string' && params?.initial !== false) {
|
|
283
308
|
return {
|
|
284
309
|
routes: [
|
|
@@ -320,9 +345,14 @@ export function useNavigationBuilder<
|
|
|
320
345
|
) {
|
|
321
346
|
const navigatorKey = useRegisterNavigator();
|
|
322
347
|
|
|
323
|
-
const route = React.
|
|
324
|
-
|
|
325
|
-
|
|
348
|
+
const route = React.use(NavigationRouteContext) as NavigatorRoute | undefined;
|
|
349
|
+
|
|
350
|
+
const consumedParams = React.use(ConsumedParamsContext);
|
|
351
|
+
|
|
352
|
+
const isNestedParamsConsumed =
|
|
353
|
+
typeof route?.params === 'object' && route.params != null
|
|
354
|
+
? consumedParams?.has(route.params)
|
|
355
|
+
: false;
|
|
326
356
|
|
|
327
357
|
const {
|
|
328
358
|
children,
|
|
@@ -366,44 +396,7 @@ export function useNavigationBuilder<
|
|
|
366
396
|
return original;
|
|
367
397
|
});
|
|
368
398
|
|
|
369
|
-
const screens = routeConfigs.reduce<
|
|
370
|
-
Record<string, ScreenConfigWithParent<State, ScreenOptions, EventMap>>
|
|
371
|
-
>((acc, config) => {
|
|
372
|
-
if (config.props.name in acc) {
|
|
373
|
-
throw new Error(
|
|
374
|
-
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
acc[config.props.name] = config;
|
|
379
|
-
return acc;
|
|
380
|
-
}, {});
|
|
381
|
-
|
|
382
399
|
const routeNames = routeConfigs.map((config) => config.props.name);
|
|
383
|
-
const routeKeyList = routeNames.reduce<Record<string, React.Key | undefined>>(
|
|
384
|
-
(acc, curr) => {
|
|
385
|
-
acc[curr] = screens[curr].keys.map((key) => key ?? '').join(':');
|
|
386
|
-
return acc;
|
|
387
|
-
},
|
|
388
|
-
{}
|
|
389
|
-
);
|
|
390
|
-
const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
|
|
391
|
-
(acc, curr) => {
|
|
392
|
-
const { initialParams } = screens[curr].props;
|
|
393
|
-
acc[curr] = initialParams;
|
|
394
|
-
return acc;
|
|
395
|
-
},
|
|
396
|
-
{}
|
|
397
|
-
);
|
|
398
|
-
const routeGetIdList = routeNames.reduce<
|
|
399
|
-
RouterConfigOptions['routeGetIdList']
|
|
400
|
-
>(
|
|
401
|
-
(acc, curr) =>
|
|
402
|
-
Object.assign(acc, {
|
|
403
|
-
[curr]: screens[curr].props.getId,
|
|
404
|
-
}),
|
|
405
|
-
{}
|
|
406
|
-
);
|
|
407
400
|
|
|
408
401
|
if (!routeNames.length) {
|
|
409
402
|
throw new Error(
|
|
@@ -411,6 +404,31 @@ export function useNavigationBuilder<
|
|
|
411
404
|
);
|
|
412
405
|
}
|
|
413
406
|
|
|
407
|
+
const screens: Record<
|
|
408
|
+
string,
|
|
409
|
+
ScreenConfigWithParent<State, ScreenOptions, EventMap>
|
|
410
|
+
> = {};
|
|
411
|
+
|
|
412
|
+
const routeKeyList: Record<string, (string | undefined)[]> = {};
|
|
413
|
+
const routeParamList: Record<string, object | undefined> = {};
|
|
414
|
+
const routeGetIdList: RouterConfigOptions['routeGetIdList'] = {};
|
|
415
|
+
|
|
416
|
+
for (const config of routeConfigs) {
|
|
417
|
+
const name = config.props.name;
|
|
418
|
+
|
|
419
|
+
if (name in screens) {
|
|
420
|
+
throw new Error(
|
|
421
|
+
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${name}')`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
screens[name] = config;
|
|
426
|
+
routeKeyList[name] = config.keys;
|
|
427
|
+
routeParamList[name] = config.props.initialParams;
|
|
428
|
+
|
|
429
|
+
Object.assign(routeGetIdList, { [name]: config.props.getId });
|
|
430
|
+
}
|
|
431
|
+
|
|
414
432
|
const isStateValid = React.useCallback(
|
|
415
433
|
(state: NavigationState | PartialState<NavigationState>) =>
|
|
416
434
|
state.type === undefined || state.type === router.type,
|
|
@@ -418,7 +436,9 @@ export function useNavigationBuilder<
|
|
|
418
436
|
);
|
|
419
437
|
|
|
420
438
|
const isStateInitialized = React.useCallback(
|
|
421
|
-
|
|
439
|
+
<T extends NavigationState>(
|
|
440
|
+
state: T | PartialState<T> | undefined
|
|
441
|
+
): state is T =>
|
|
422
442
|
state !== undefined && state.stale === false && isStateValid(state),
|
|
423
443
|
[isStateValid]
|
|
424
444
|
);
|
|
@@ -433,19 +453,30 @@ export function useNavigationBuilder<
|
|
|
433
453
|
state: currentState,
|
|
434
454
|
getState: getCurrentState,
|
|
435
455
|
setState: setCurrentState,
|
|
456
|
+
subscribe,
|
|
436
457
|
setKey,
|
|
437
458
|
getKey,
|
|
438
459
|
getIsInitial,
|
|
439
|
-
} = React.
|
|
460
|
+
} = React.use(NavigationStateContext);
|
|
440
461
|
|
|
441
|
-
const
|
|
462
|
+
const { onEmitEvent, getIsStateEmitted } = React.use(
|
|
463
|
+
NavigationBuilderContext
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const stateCleanupRef = React.useRef<boolean>(false);
|
|
467
|
+
const lastStateRef = React.useRef<State | PartialState<State> | undefined>(
|
|
468
|
+
undefined
|
|
469
|
+
);
|
|
442
470
|
|
|
443
471
|
const setState = useLatestCallback(
|
|
444
|
-
(state:
|
|
445
|
-
if (
|
|
472
|
+
(state: State | PartialState<State> | undefined) => {
|
|
473
|
+
if (stateCleanupRef.current) {
|
|
474
|
+
// Store the state locally in case the current navigator is in `Activity`
|
|
475
|
+
lastStateRef.current = state;
|
|
476
|
+
|
|
446
477
|
// State might have been already cleaned up due to unmount
|
|
447
|
-
// We
|
|
448
|
-
//
|
|
478
|
+
// We don't want to update `route.state` in parent
|
|
479
|
+
// Otherwise it will be reused if a new navigator gets mounted
|
|
449
480
|
return;
|
|
450
481
|
}
|
|
451
482
|
|
|
@@ -457,15 +488,36 @@ export function useNavigationBuilder<
|
|
|
457
488
|
stateBeforeInitialization,
|
|
458
489
|
initializedState,
|
|
459
490
|
isFirstStateInitialization,
|
|
491
|
+
paramsUsedForInitialization,
|
|
460
492
|
] = React.useMemo((): [
|
|
461
493
|
PartialState<State> | undefined,
|
|
462
494
|
State | undefined,
|
|
463
495
|
boolean,
|
|
496
|
+
object | undefined,
|
|
464
497
|
] => {
|
|
498
|
+
// If the state was already cleaned up, but we have it stored in ref,
|
|
499
|
+
// It likely got cleaned up due to `<Activity mode="hidden">`
|
|
500
|
+
// We should reuse this state to avoid remounting screens
|
|
501
|
+
if (
|
|
502
|
+
stateCleanupRef.current &&
|
|
503
|
+
lastStateRef.current &&
|
|
504
|
+
isStateValid(lastStateRef.current)
|
|
505
|
+
) {
|
|
506
|
+
const state: State = isStateInitialized(lastStateRef.current)
|
|
507
|
+
? lastStateRef.current
|
|
508
|
+
: router.getRehydratedState(lastStateRef.current, {
|
|
509
|
+
routeNames,
|
|
510
|
+
routeParamList,
|
|
511
|
+
routeGetIdList,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
return [undefined, state, false, undefined];
|
|
515
|
+
}
|
|
516
|
+
|
|
465
517
|
const initialRouteParamList = routeNames.reduce<
|
|
466
518
|
Record<string, object | undefined>
|
|
467
519
|
>((acc, curr) => {
|
|
468
|
-
const { initialParams } = screens[curr]
|
|
520
|
+
const { initialParams } = screens[curr]?.props ?? {};
|
|
469
521
|
const initialParamsFromParams =
|
|
470
522
|
route?.params?.state == null &&
|
|
471
523
|
route?.params?.initial !== false &&
|
|
@@ -494,7 +546,8 @@ export function useNavigationBuilder<
|
|
|
494
546
|
!(
|
|
495
547
|
typeof route?.params?.screen === 'string' &&
|
|
496
548
|
route?.params?.initial !== false
|
|
497
|
-
)
|
|
549
|
+
) &&
|
|
550
|
+
!isNestedParamsConsumed
|
|
498
551
|
) {
|
|
499
552
|
return [
|
|
500
553
|
undefined,
|
|
@@ -504,28 +557,48 @@ export function useNavigationBuilder<
|
|
|
504
557
|
routeGetIdList,
|
|
505
558
|
}),
|
|
506
559
|
true,
|
|
560
|
+
undefined,
|
|
507
561
|
];
|
|
508
562
|
} else {
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
563
|
+
const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
|
|
564
|
+
const stateFromParams = paramsForState
|
|
565
|
+
? getStateFromParams(paramsForState, router.type)
|
|
566
|
+
: undefined;
|
|
567
|
+
|
|
568
|
+
const stateBeforeInitialization = (stateFromParams ?? currentState) as
|
|
569
|
+
| PartialState<State>
|
|
570
|
+
| undefined;
|
|
571
|
+
|
|
572
|
+
const hydratedState =
|
|
573
|
+
stateBeforeInitialization == null
|
|
574
|
+
? router.getInitialState({
|
|
575
|
+
routeNames,
|
|
576
|
+
routeParamList: initialRouteParamList,
|
|
577
|
+
routeGetIdList,
|
|
578
|
+
})
|
|
579
|
+
: router.getRehydratedState(stateBeforeInitialization, {
|
|
580
|
+
routeNames,
|
|
581
|
+
routeParamList: initialRouteParamList,
|
|
582
|
+
routeGetIdList,
|
|
583
|
+
});
|
|
520
584
|
|
|
521
585
|
if (
|
|
586
|
+
stateBeforeInitialization != null &&
|
|
587
|
+
isStateValid(stateBeforeInitialization) &&
|
|
522
588
|
options.routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
523
589
|
doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)
|
|
524
590
|
) {
|
|
525
|
-
return [stateBeforeInitialization, hydratedState, true];
|
|
591
|
+
return [stateBeforeInitialization, hydratedState, true, paramsForState];
|
|
526
592
|
}
|
|
527
593
|
|
|
528
|
-
return [
|
|
594
|
+
return [
|
|
595
|
+
undefined,
|
|
596
|
+
hydratedState,
|
|
597
|
+
false,
|
|
598
|
+
// Current state is already initialized
|
|
599
|
+
// So params weren't used, and need to be handled later
|
|
600
|
+
isStateInitialized(currentState) ? undefined : paramsForState,
|
|
601
|
+
];
|
|
529
602
|
}
|
|
530
603
|
// We explicitly don't include routeNames, route.params etc. in the dep list
|
|
531
604
|
// below. We want to avoid forcing a new state to be calculated in those cases
|
|
@@ -537,12 +610,22 @@ export function useNavigationBuilder<
|
|
|
537
610
|
|
|
538
611
|
const previousRouteKeyListRef = React.useRef(routeKeyList);
|
|
539
612
|
|
|
540
|
-
React.
|
|
613
|
+
React.useInsertionEffect(() => {
|
|
541
614
|
previousRouteKeyListRef.current = routeKeyList;
|
|
542
615
|
});
|
|
543
616
|
|
|
544
617
|
const previousRouteKeyList = previousRouteKeyListRef.current;
|
|
545
618
|
|
|
619
|
+
// Names of the screens whose navigation key changed since the last render
|
|
620
|
+
const routeKeyChanges = Object.keys(routeKeyList).filter((name) => {
|
|
621
|
+
const current = routeKeyList[name];
|
|
622
|
+
const previous = previousRouteKeyList[name];
|
|
623
|
+
|
|
624
|
+
return (
|
|
625
|
+
current != null && previous != null && !isArrayEqual(current, previous)
|
|
626
|
+
);
|
|
627
|
+
});
|
|
628
|
+
|
|
546
629
|
const [unhandledState, setUnhandledState] = React.useState<
|
|
547
630
|
NavigationState | PartialState<NavigationState> | undefined
|
|
548
631
|
>(stateBeforeInitialization);
|
|
@@ -570,10 +653,14 @@ export function useNavigationBuilder<
|
|
|
570
653
|
let nextState: State = state;
|
|
571
654
|
let shouldClearUnhandledState = false;
|
|
572
655
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
656
|
+
if (unhandledState != null && !isStateValid(unhandledState)) {
|
|
657
|
+
// The stored unhandled state is for a different type of navigator
|
|
658
|
+
// It can't be used with this navigator, so we discard it
|
|
659
|
+
setUnhandledState(undefined);
|
|
660
|
+
} else if (
|
|
661
|
+
// Previously unhandled state is now valid again
|
|
662
|
+
// And current state no longer has any valid routes
|
|
663
|
+
// We should reuse the unhandled state instead of re-calculating the state
|
|
577
664
|
unhandledState?.routes.every((r) => routeNames.includes(r.name)) &&
|
|
578
665
|
state?.routes.every((r) => !routeNames.includes(r.name))
|
|
579
666
|
) {
|
|
@@ -588,71 +675,68 @@ export function useNavigationBuilder<
|
|
|
588
675
|
);
|
|
589
676
|
} else if (
|
|
590
677
|
!isArrayEqual(state.routeNames, routeNames) ||
|
|
591
|
-
|
|
678
|
+
routeKeyChanges.length !== 0
|
|
592
679
|
) {
|
|
593
680
|
// When the list of route names change, the router should handle it to remove invalid routes
|
|
594
681
|
nextState = router.getStateForRouteNamesChange(state, {
|
|
595
682
|
routeNames,
|
|
596
683
|
routeParamList,
|
|
597
684
|
routeGetIdList,
|
|
598
|
-
routeKeyChanges
|
|
599
|
-
(name) =>
|
|
600
|
-
name in previousRouteKeyList &&
|
|
601
|
-
routeKeyList[name] !== previousRouteKeyList[name]
|
|
602
|
-
),
|
|
685
|
+
routeKeyChanges,
|
|
603
686
|
});
|
|
604
687
|
}
|
|
605
688
|
|
|
606
|
-
|
|
689
|
+
let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
|
|
607
690
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}, [route?.params]);
|
|
691
|
+
if (route?.params && !didConsumeNestedParams) {
|
|
692
|
+
let action: CommonActions.Action | undefined;
|
|
611
693
|
|
|
612
|
-
|
|
613
|
-
|
|
694
|
+
if (isNavigationState(route.params.state) && !isNestedParamsConsumed) {
|
|
695
|
+
didConsumeNestedParams = true;
|
|
614
696
|
|
|
615
|
-
|
|
697
|
+
const stateFromParams = getStateFromParams(route.params, router.type);
|
|
616
698
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
699
|
+
if (stateFromParams != null) {
|
|
700
|
+
if (
|
|
701
|
+
options.routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
702
|
+
doesStateHaveOnlyInvalidRoutes(stateFromParams)
|
|
703
|
+
) {
|
|
704
|
+
if (stateFromParams !== unhandledState) {
|
|
705
|
+
setUnhandledState(stateFromParams);
|
|
706
|
+
}
|
|
707
|
+
} else {
|
|
708
|
+
// If the route was updated with new state, we should reset to it
|
|
709
|
+
action = CommonActions.reset(stateFromParams);
|
|
628
710
|
}
|
|
629
|
-
} else {
|
|
630
|
-
// If the route was updated with new state, we should reset to it
|
|
631
|
-
action = CommonActions.reset(route.params.state);
|
|
632
711
|
}
|
|
633
712
|
} else if (
|
|
634
713
|
typeof route.params.screen === 'string' &&
|
|
635
714
|
((route.params.initial === false && isFirstStateInitialization) ||
|
|
636
|
-
|
|
715
|
+
!isNestedParamsConsumed)
|
|
637
716
|
) {
|
|
717
|
+
didConsumeNestedParams = true;
|
|
718
|
+
|
|
638
719
|
if (
|
|
639
720
|
options.routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
640
721
|
!routeNames.includes(route.params.screen)
|
|
641
722
|
) {
|
|
642
|
-
const state = getStateFromParams(route.params);
|
|
723
|
+
const state = getStateFromParams(route.params, router.type);
|
|
643
724
|
|
|
644
725
|
if (state != null && !deepEqual(state, unhandledState)) {
|
|
645
726
|
setUnhandledState(state);
|
|
646
727
|
}
|
|
647
728
|
} else {
|
|
648
729
|
// If the route was updated with new screen name and/or params, we should navigate there
|
|
649
|
-
action =
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
730
|
+
action = {
|
|
731
|
+
type: 'NAVIGATE',
|
|
732
|
+
payload: {
|
|
733
|
+
name: route.params.screen,
|
|
734
|
+
params: route.params.params,
|
|
735
|
+
path: route.params.path,
|
|
736
|
+
merge: route.params.merge,
|
|
737
|
+
pop: route.params.pop,
|
|
738
|
+
},
|
|
739
|
+
} as const;
|
|
656
740
|
}
|
|
657
741
|
}
|
|
658
742
|
|
|
@@ -665,6 +749,18 @@ export function useNavigationBuilder<
|
|
|
665
749
|
})
|
|
666
750
|
: null;
|
|
667
751
|
|
|
752
|
+
if (
|
|
753
|
+
process.env.NODE_ENV !== 'production' &&
|
|
754
|
+
action != null &&
|
|
755
|
+
updatedState === null
|
|
756
|
+
) {
|
|
757
|
+
const names = routeNames.map((name) => `'${name}'`).join(', ');
|
|
758
|
+
|
|
759
|
+
console.error(
|
|
760
|
+
`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.`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
668
764
|
nextState =
|
|
669
765
|
updatedState !== null
|
|
670
766
|
? router.getRehydratedState(updatedState, {
|
|
@@ -675,14 +771,22 @@ export function useNavigationBuilder<
|
|
|
675
771
|
: nextState;
|
|
676
772
|
}
|
|
677
773
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
774
|
+
React.useEffect(() => {
|
|
775
|
+
if (
|
|
776
|
+
consumedParams &&
|
|
777
|
+
didConsumeNestedParams &&
|
|
778
|
+
typeof route?.params === 'object' &&
|
|
779
|
+
route.params != null
|
|
780
|
+
) {
|
|
781
|
+
consumedParams.set(route.params, true);
|
|
782
|
+
}
|
|
783
|
+
}, [consumedParams, didConsumeNestedParams, route?.params]);
|
|
784
|
+
|
|
785
|
+
const shouldUpdate = state !== nextState;
|
|
682
786
|
|
|
683
787
|
useScheduleUpdate(() => {
|
|
684
788
|
if (shouldUpdate) {
|
|
685
|
-
//
|
|
789
|
+
// Schedule an update if the state needs to be updated
|
|
686
790
|
setState(nextState);
|
|
687
791
|
|
|
688
792
|
if (shouldClearUnhandledState) {
|
|
@@ -696,43 +800,59 @@ export function useNavigationBuilder<
|
|
|
696
800
|
// So we override the state object we return to use the latest state as soon as possible
|
|
697
801
|
state = nextState;
|
|
698
802
|
|
|
803
|
+
// Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
|
|
804
|
+
React.useEffect(() => {
|
|
805
|
+
lastStateRef.current = state;
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const lastNotifiedStateRef = React.useRef<State | null>(null);
|
|
809
|
+
|
|
699
810
|
React.useEffect(() => {
|
|
700
811
|
// In strict mode, React will double-invoke effects.
|
|
701
812
|
// So we need to reset the flag if component was not unmounted
|
|
702
|
-
|
|
813
|
+
stateCleanupRef.current = false;
|
|
703
814
|
|
|
704
815
|
setKey(navigatorKey);
|
|
705
816
|
|
|
706
|
-
if (
|
|
707
|
-
|
|
708
|
-
|
|
817
|
+
if (
|
|
818
|
+
(!getIsInitial() || getIsStateEmitted()) &&
|
|
819
|
+
lastNotifiedStateRef.current !== state
|
|
820
|
+
) {
|
|
821
|
+
// We need to notify the state update in these scenarios:
|
|
822
|
+
// 1. If it's not the initial render of the component containing the navigator
|
|
823
|
+
// 2. If the container has already emitted state before the navigator mounted
|
|
824
|
+
//
|
|
825
|
+
// This will make sure that the container gets notified of state changes due to new mounts
|
|
709
826
|
// This is necessary for proper screen tracking, URL updates etc.
|
|
710
|
-
|
|
827
|
+
//
|
|
828
|
+
// During hydration, a nested navigator may mount after the container's initial state event.
|
|
829
|
+
// Or mount could be delayed due to conditional rendering, suspense etc.
|
|
830
|
+
// So only checking for the initial render is not enough.
|
|
831
|
+
// We also need to propagate the navigator's initial state when it mounts later,
|
|
832
|
+
// so the container is notified that the nested state is now available.
|
|
833
|
+
//
|
|
834
|
+
// We only notify if the state is different from what we already notified
|
|
835
|
+
// Otherwise, this goes into a loop when inside `<Activity mode="hidden">`
|
|
836
|
+
setState(state);
|
|
837
|
+
|
|
838
|
+
lastNotifiedStateRef.current = state;
|
|
711
839
|
}
|
|
712
840
|
|
|
713
841
|
return () => {
|
|
714
842
|
// We need to clean up state for this navigator on unmount
|
|
715
843
|
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
|
716
844
|
setCurrentState(undefined);
|
|
717
|
-
|
|
845
|
+
stateCleanupRef.current = true;
|
|
718
846
|
}
|
|
847
|
+
|
|
848
|
+
// Reset so that StrictMode's second mount re-propagates state correctly.
|
|
849
|
+
// Without this, the guard above sees the same reference and skips setState,
|
|
850
|
+
// causing the initial state to be lost after cleanup wipes the container state.
|
|
851
|
+
lastNotifiedStateRef.current = null;
|
|
719
852
|
};
|
|
720
853
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
721
854
|
}, []);
|
|
722
855
|
|
|
723
|
-
// In some cases (e.g. route names change), internal state might have changed
|
|
724
|
-
// But it hasn't been committed yet, so hasn't propagated to the sync external store
|
|
725
|
-
// During this time, we need to return the internal state in `getState`
|
|
726
|
-
// Otherwise it can result in inconsistent state during render in children
|
|
727
|
-
// To avoid this, we use a ref for render phase, and immediately clear it on commit
|
|
728
|
-
const stateRef = React.useRef<State | null>(state);
|
|
729
|
-
|
|
730
|
-
stateRef.current = state;
|
|
731
|
-
|
|
732
|
-
React.useLayoutEffect(() => {
|
|
733
|
-
stateRef.current = null;
|
|
734
|
-
});
|
|
735
|
-
|
|
736
856
|
const getState = useLatestCallback((): State => {
|
|
737
857
|
const currentState = getCurrentState();
|
|
738
858
|
|
|
@@ -743,8 +863,6 @@ export function useNavigationBuilder<
|
|
|
743
863
|
);
|
|
744
864
|
});
|
|
745
865
|
|
|
746
|
-
const { onEmitEvent } = React.useContext(NavigationBuilderContext);
|
|
747
|
-
|
|
748
866
|
const emitter = useEventEmitter<EventMapCore<State>>((e) => {
|
|
749
867
|
const routeNames = [];
|
|
750
868
|
|
|
@@ -767,43 +885,48 @@ export function useNavigationBuilder<
|
|
|
767
885
|
return;
|
|
768
886
|
}
|
|
769
887
|
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
.concat(
|
|
774
|
-
// Get an array of listeners for all screens + common listeners on navigator
|
|
775
|
-
...[
|
|
776
|
-
screenListeners,
|
|
777
|
-
...routeNames.map((name) => {
|
|
778
|
-
const { listeners } = screens[name].props;
|
|
779
|
-
return listeners;
|
|
780
|
-
}),
|
|
781
|
-
].map((listeners) => {
|
|
782
|
-
const map =
|
|
783
|
-
typeof listeners === 'function'
|
|
784
|
-
? listeners({ route: route as any, navigation })
|
|
785
|
-
: listeners;
|
|
786
|
-
|
|
787
|
-
return map
|
|
788
|
-
? Object.keys(map)
|
|
789
|
-
.filter((type) => type === e.type)
|
|
790
|
-
.map((type) => map?.[type])
|
|
791
|
-
: undefined;
|
|
792
|
-
})
|
|
793
|
-
)
|
|
794
|
-
// We don't want same listener to be called multiple times for same event
|
|
795
|
-
// So we remove any duplicate functions from the array
|
|
796
|
-
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
|
888
|
+
const hasPerScreenListeners = routeNames.some(
|
|
889
|
+
(name) => screens[name]?.props.listeners != null
|
|
890
|
+
);
|
|
797
891
|
|
|
798
|
-
|
|
892
|
+
if (screenListeners != null || hasPerScreenListeners) {
|
|
893
|
+
const descriptor = descriptors[route.key];
|
|
799
894
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
895
|
+
if (descriptor == null) {
|
|
896
|
+
throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const navigation = descriptor.navigation;
|
|
900
|
+
|
|
901
|
+
const listeners = ([] as (((e: any) => void) | undefined)[])
|
|
902
|
+
.concat(
|
|
903
|
+
// Get an array of listeners for all screens + common listeners on navigator
|
|
904
|
+
...[
|
|
905
|
+
screenListeners,
|
|
906
|
+
...routeNames.map((name) => {
|
|
907
|
+
const { listeners } = screens[name]?.props ?? {};
|
|
908
|
+
return listeners;
|
|
909
|
+
}),
|
|
910
|
+
].map((listeners) => {
|
|
911
|
+
const map =
|
|
912
|
+
typeof listeners === 'function'
|
|
913
|
+
? listeners({ route: route as any, navigation })
|
|
914
|
+
: listeners;
|
|
915
|
+
|
|
916
|
+
return map
|
|
917
|
+
? Object.keys(map)
|
|
918
|
+
.filter((type) => type === e.type)
|
|
919
|
+
.map((type) => map?.[type])
|
|
920
|
+
: undefined;
|
|
921
|
+
})
|
|
922
|
+
)
|
|
923
|
+
// We don't want same listener to be called multiple times for same event
|
|
924
|
+
// So we remove any duplicate functions from the array
|
|
925
|
+
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
|
926
|
+
|
|
927
|
+
listeners.forEach((listener) => listener?.(e));
|
|
928
|
+
}
|
|
929
|
+
}, onEmitEvent);
|
|
807
930
|
|
|
808
931
|
useFocusEvents({ state, emitter });
|
|
809
932
|
|
|
@@ -837,7 +960,7 @@ export function useNavigationBuilder<
|
|
|
837
960
|
setState,
|
|
838
961
|
});
|
|
839
962
|
|
|
840
|
-
const onUnhandledActionParent = React.
|
|
963
|
+
const onUnhandledActionParent = React.use(UnhandledActionContext);
|
|
841
964
|
|
|
842
965
|
const onUnhandledAction = useLatestCallback((action: NavigationAction) => {
|
|
843
966
|
if (
|
|
@@ -882,9 +1005,9 @@ export function useNavigationBuilder<
|
|
|
882
1005
|
onAction,
|
|
883
1006
|
onUnhandledAction,
|
|
884
1007
|
getState,
|
|
1008
|
+
state,
|
|
885
1009
|
emitter,
|
|
886
1010
|
router,
|
|
887
|
-
stateRef,
|
|
888
1011
|
});
|
|
889
1012
|
|
|
890
1013
|
useFocusedListenersChildrenAdapter({
|
|
@@ -903,9 +1026,7 @@ export function useNavigationBuilder<
|
|
|
903
1026
|
ScreenOptions,
|
|
904
1027
|
EventMap
|
|
905
1028
|
>({
|
|
906
|
-
routes:
|
|
907
|
-
? router.getRoutesFromState(state)
|
|
908
|
-
: state.routes,
|
|
1029
|
+
routes: state.routes,
|
|
909
1030
|
screens,
|
|
910
1031
|
navigation,
|
|
911
1032
|
screenOptions,
|
|
@@ -913,6 +1034,7 @@ export function useNavigationBuilder<
|
|
|
913
1034
|
onAction,
|
|
914
1035
|
getState,
|
|
915
1036
|
setState,
|
|
1037
|
+
subscribe,
|
|
916
1038
|
onRouteFocus,
|
|
917
1039
|
addListener,
|
|
918
1040
|
addKeyedListener,
|
|
@@ -921,13 +1043,13 @@ export function useNavigationBuilder<
|
|
|
921
1043
|
emitter,
|
|
922
1044
|
});
|
|
923
1045
|
|
|
924
|
-
useCurrentRender({
|
|
925
|
-
state,
|
|
926
|
-
navigation,
|
|
927
|
-
descriptors,
|
|
928
|
-
});
|
|
929
|
-
|
|
930
1046
|
const NavigationContent = useComponent((children: React.ReactNode) => {
|
|
1047
|
+
const focusedRoute = state.routes[state.index];
|
|
1048
|
+
|
|
1049
|
+
if (focusedRoute == null) {
|
|
1050
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
931
1053
|
const element =
|
|
932
1054
|
layout != null
|
|
933
1055
|
? layout({
|
|
@@ -941,8 +1063,14 @@ export function useNavigationBuilder<
|
|
|
941
1063
|
return (
|
|
942
1064
|
<NavigationMetaContext.Provider value={undefined}>
|
|
943
1065
|
<NavigationHelpersContext.Provider value={navigation}>
|
|
944
|
-
<NavigationStateListenerProvider
|
|
945
|
-
|
|
1066
|
+
<NavigationStateListenerProvider
|
|
1067
|
+
state={state}
|
|
1068
|
+
getState={navigation.getState}
|
|
1069
|
+
subscribe={subscribe}
|
|
1070
|
+
>
|
|
1071
|
+
<FocusedRouteKeyContext.Provider value={focusedRoute.key}>
|
|
1072
|
+
<PreventRemoveProvider>{element}</PreventRemoveProvider>
|
|
1073
|
+
</FocusedRouteKeyContext.Provider>
|
|
946
1074
|
</NavigationStateListenerProvider>
|
|
947
1075
|
</NavigationHelpersContext.Provider>
|
|
948
1076
|
</NavigationMetaContext.Provider>
|