@react-navigation/core 8.0.0-alpha.3 → 8.0.0-alpha.30
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 +34 -17
- 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 +17 -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.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 +7 -40
- 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 +485 -214
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/getStateFromRouteParams.js +24 -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 +19 -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 +201 -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 +70 -35
- 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 +7 -3
- 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 +3 -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 +3 -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 -3
- 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 +5 -5
- 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 +8 -8
- 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 +13 -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.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 +351 -333
- 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 +18 -12
- package/src/NavigationProvider.tsx +26 -15
- package/src/NavigationStateContext.tsx +5 -6
- package/src/PreventRemoveContext.tsx +1 -0
- package/src/PreventRemoveProvider.tsx +51 -46
- package/src/SceneView.tsx +7 -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 +13 -8
- package/src/getActionFromState.tsx +86 -51
- package/src/getPathFromState.tsx +254 -128
- package/src/getPatternParts.tsx +25 -4
- package/src/getStateFromPath.tsx +811 -309
- package/src/getStateFromRouteParams.tsx +60 -0
- package/src/index.tsx +55 -5
- package/src/theming/useTheme.tsx +1 -1
- package/src/types.tsx +798 -373
- package/src/useDescriptors.tsx +28 -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 +311 -186
- package/src/useNavigationCache.tsx +32 -25
- package/src/useNavigationHelpers.tsx +27 -10
- package/src/useNavigationIndependentTree.tsx +1 -1
- package/src/useNavigationState.tsx +93 -59
- 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 +8 -3
- 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
|
);
|
|
@@ -436,16 +456,26 @@ export function useNavigationBuilder<
|
|
|
436
456
|
setKey,
|
|
437
457
|
getKey,
|
|
438
458
|
getIsInitial,
|
|
439
|
-
} = React.
|
|
459
|
+
} = React.use(NavigationStateContext);
|
|
440
460
|
|
|
441
|
-
const
|
|
461
|
+
const { onEmitEvent, getIsStateEmitted } = React.use(
|
|
462
|
+
NavigationBuilderContext
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
const stateCleanupRef = React.useRef<boolean>(false);
|
|
466
|
+
const lastStateRef = React.useRef<State | PartialState<State> | undefined>(
|
|
467
|
+
undefined
|
|
468
|
+
);
|
|
442
469
|
|
|
443
470
|
const setState = useLatestCallback(
|
|
444
|
-
(state:
|
|
445
|
-
if (
|
|
471
|
+
(state: State | PartialState<State> | undefined) => {
|
|
472
|
+
if (stateCleanupRef.current) {
|
|
473
|
+
// Store the state locally in case the current navigator is in `Activity`
|
|
474
|
+
lastStateRef.current = state;
|
|
475
|
+
|
|
446
476
|
// State might have been already cleaned up due to unmount
|
|
447
|
-
// We
|
|
448
|
-
//
|
|
477
|
+
// We don't want to update `route.state` in parent
|
|
478
|
+
// Otherwise it will be reused if a new navigator gets mounted
|
|
449
479
|
return;
|
|
450
480
|
}
|
|
451
481
|
|
|
@@ -457,15 +487,36 @@ export function useNavigationBuilder<
|
|
|
457
487
|
stateBeforeInitialization,
|
|
458
488
|
initializedState,
|
|
459
489
|
isFirstStateInitialization,
|
|
490
|
+
paramsUsedForInitialization,
|
|
460
491
|
] = React.useMemo((): [
|
|
461
492
|
PartialState<State> | undefined,
|
|
462
493
|
State | undefined,
|
|
463
494
|
boolean,
|
|
495
|
+
object | undefined,
|
|
464
496
|
] => {
|
|
497
|
+
// If the state was already cleaned up, but we have it stored in ref,
|
|
498
|
+
// It likely got cleaned up due to `<Activity mode="hidden">`
|
|
499
|
+
// We should reuse this state to avoid remounting screens
|
|
500
|
+
if (
|
|
501
|
+
stateCleanupRef.current &&
|
|
502
|
+
lastStateRef.current &&
|
|
503
|
+
isStateValid(lastStateRef.current)
|
|
504
|
+
) {
|
|
505
|
+
const state: State = isStateInitialized(lastStateRef.current)
|
|
506
|
+
? lastStateRef.current
|
|
507
|
+
: router.getRehydratedState(lastStateRef.current, {
|
|
508
|
+
routeNames,
|
|
509
|
+
routeParamList,
|
|
510
|
+
routeGetIdList,
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
return [undefined, state, false, undefined];
|
|
514
|
+
}
|
|
515
|
+
|
|
465
516
|
const initialRouteParamList = routeNames.reduce<
|
|
466
517
|
Record<string, object | undefined>
|
|
467
518
|
>((acc, curr) => {
|
|
468
|
-
const { initialParams } = screens[curr]
|
|
519
|
+
const { initialParams } = screens[curr]?.props ?? {};
|
|
469
520
|
const initialParamsFromParams =
|
|
470
521
|
route?.params?.state == null &&
|
|
471
522
|
route?.params?.initial !== false &&
|
|
@@ -494,7 +545,8 @@ export function useNavigationBuilder<
|
|
|
494
545
|
!(
|
|
495
546
|
typeof route?.params?.screen === 'string' &&
|
|
496
547
|
route?.params?.initial !== false
|
|
497
|
-
)
|
|
548
|
+
) &&
|
|
549
|
+
!isNestedParamsConsumed
|
|
498
550
|
) {
|
|
499
551
|
return [
|
|
500
552
|
undefined,
|
|
@@ -504,28 +556,48 @@ export function useNavigationBuilder<
|
|
|
504
556
|
routeGetIdList,
|
|
505
557
|
}),
|
|
506
558
|
true,
|
|
559
|
+
undefined,
|
|
507
560
|
];
|
|
508
561
|
} else {
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
562
|
+
const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
|
|
563
|
+
const stateFromParams = paramsForState
|
|
564
|
+
? getStateFromParams(paramsForState, router.type)
|
|
565
|
+
: undefined;
|
|
566
|
+
|
|
567
|
+
const stateBeforeInitialization = (stateFromParams ?? currentState) as
|
|
568
|
+
| PartialState<State>
|
|
569
|
+
| undefined;
|
|
570
|
+
|
|
571
|
+
const hydratedState =
|
|
572
|
+
stateBeforeInitialization == null
|
|
573
|
+
? router.getInitialState({
|
|
574
|
+
routeNames,
|
|
575
|
+
routeParamList: initialRouteParamList,
|
|
576
|
+
routeGetIdList,
|
|
577
|
+
})
|
|
578
|
+
: router.getRehydratedState(stateBeforeInitialization, {
|
|
579
|
+
routeNames,
|
|
580
|
+
routeParamList: initialRouteParamList,
|
|
581
|
+
routeGetIdList,
|
|
582
|
+
});
|
|
520
583
|
|
|
521
584
|
if (
|
|
585
|
+
stateBeforeInitialization != null &&
|
|
586
|
+
isStateValid(stateBeforeInitialization) &&
|
|
522
587
|
options.routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
523
588
|
doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)
|
|
524
589
|
) {
|
|
525
|
-
return [stateBeforeInitialization, hydratedState, true];
|
|
590
|
+
return [stateBeforeInitialization, hydratedState, true, paramsForState];
|
|
526
591
|
}
|
|
527
592
|
|
|
528
|
-
return [
|
|
593
|
+
return [
|
|
594
|
+
undefined,
|
|
595
|
+
hydratedState,
|
|
596
|
+
false,
|
|
597
|
+
// Current state is already initialized
|
|
598
|
+
// So params weren't used, and need to be handled later
|
|
599
|
+
isStateInitialized(currentState) ? undefined : paramsForState,
|
|
600
|
+
];
|
|
529
601
|
}
|
|
530
602
|
// We explicitly don't include routeNames, route.params etc. in the dep list
|
|
531
603
|
// below. We want to avoid forcing a new state to be calculated in those cases
|
|
@@ -537,12 +609,22 @@ export function useNavigationBuilder<
|
|
|
537
609
|
|
|
538
610
|
const previousRouteKeyListRef = React.useRef(routeKeyList);
|
|
539
611
|
|
|
540
|
-
React.
|
|
612
|
+
React.useInsertionEffect(() => {
|
|
541
613
|
previousRouteKeyListRef.current = routeKeyList;
|
|
542
614
|
});
|
|
543
615
|
|
|
544
616
|
const previousRouteKeyList = previousRouteKeyListRef.current;
|
|
545
617
|
|
|
618
|
+
// Names of the screens whose navigation key changed since the last render
|
|
619
|
+
const routeKeyChanges = Object.keys(routeKeyList).filter((name) => {
|
|
620
|
+
const current = routeKeyList[name];
|
|
621
|
+
const previous = previousRouteKeyList[name];
|
|
622
|
+
|
|
623
|
+
return (
|
|
624
|
+
current != null && previous != null && !isArrayEqual(current, previous)
|
|
625
|
+
);
|
|
626
|
+
});
|
|
627
|
+
|
|
546
628
|
const [unhandledState, setUnhandledState] = React.useState<
|
|
547
629
|
NavigationState | PartialState<NavigationState> | undefined
|
|
548
630
|
>(stateBeforeInitialization);
|
|
@@ -570,10 +652,14 @@ export function useNavigationBuilder<
|
|
|
570
652
|
let nextState: State = state;
|
|
571
653
|
let shouldClearUnhandledState = false;
|
|
572
654
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
655
|
+
if (unhandledState != null && !isStateValid(unhandledState)) {
|
|
656
|
+
// The stored unhandled state is for a different type of navigator
|
|
657
|
+
// It can't be used with this navigator, so we discard it
|
|
658
|
+
setUnhandledState(undefined);
|
|
659
|
+
} else if (
|
|
660
|
+
// Previously unhandled state is now valid again
|
|
661
|
+
// And current state no longer has any valid routes
|
|
662
|
+
// We should reuse the unhandled state instead of re-calculating the state
|
|
577
663
|
unhandledState?.routes.every((r) => routeNames.includes(r.name)) &&
|
|
578
664
|
state?.routes.every((r) => !routeNames.includes(r.name))
|
|
579
665
|
) {
|
|
@@ -588,71 +674,68 @@ export function useNavigationBuilder<
|
|
|
588
674
|
);
|
|
589
675
|
} else if (
|
|
590
676
|
!isArrayEqual(state.routeNames, routeNames) ||
|
|
591
|
-
|
|
677
|
+
routeKeyChanges.length !== 0
|
|
592
678
|
) {
|
|
593
679
|
// When the list of route names change, the router should handle it to remove invalid routes
|
|
594
680
|
nextState = router.getStateForRouteNamesChange(state, {
|
|
595
681
|
routeNames,
|
|
596
682
|
routeParamList,
|
|
597
683
|
routeGetIdList,
|
|
598
|
-
routeKeyChanges
|
|
599
|
-
(name) =>
|
|
600
|
-
name in previousRouteKeyList &&
|
|
601
|
-
routeKeyList[name] !== previousRouteKeyList[name]
|
|
602
|
-
),
|
|
684
|
+
routeKeyChanges,
|
|
603
685
|
});
|
|
604
686
|
}
|
|
605
687
|
|
|
606
|
-
|
|
688
|
+
let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
|
|
607
689
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}, [route?.params]);
|
|
690
|
+
if (route?.params && !didConsumeNestedParams) {
|
|
691
|
+
let action: CommonActions.Action | undefined;
|
|
611
692
|
|
|
612
|
-
|
|
613
|
-
|
|
693
|
+
if (isNavigationState(route.params.state) && !isNestedParamsConsumed) {
|
|
694
|
+
didConsumeNestedParams = true;
|
|
614
695
|
|
|
615
|
-
|
|
696
|
+
const stateFromParams = getStateFromParams(route.params, router.type);
|
|
616
697
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
698
|
+
if (stateFromParams != null) {
|
|
699
|
+
if (
|
|
700
|
+
options.routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
701
|
+
doesStateHaveOnlyInvalidRoutes(stateFromParams)
|
|
702
|
+
) {
|
|
703
|
+
if (stateFromParams !== unhandledState) {
|
|
704
|
+
setUnhandledState(stateFromParams);
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
// If the route was updated with new state, we should reset to it
|
|
708
|
+
action = CommonActions.reset(stateFromParams);
|
|
628
709
|
}
|
|
629
|
-
} else {
|
|
630
|
-
// If the route was updated with new state, we should reset to it
|
|
631
|
-
action = CommonActions.reset(route.params.state);
|
|
632
710
|
}
|
|
633
711
|
} else if (
|
|
634
712
|
typeof route.params.screen === 'string' &&
|
|
635
713
|
((route.params.initial === false && isFirstStateInitialization) ||
|
|
636
|
-
|
|
714
|
+
!isNestedParamsConsumed)
|
|
637
715
|
) {
|
|
716
|
+
didConsumeNestedParams = true;
|
|
717
|
+
|
|
638
718
|
if (
|
|
639
719
|
options.routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
640
720
|
!routeNames.includes(route.params.screen)
|
|
641
721
|
) {
|
|
642
|
-
const state = getStateFromParams(route.params);
|
|
722
|
+
const state = getStateFromParams(route.params, router.type);
|
|
643
723
|
|
|
644
724
|
if (state != null && !deepEqual(state, unhandledState)) {
|
|
645
725
|
setUnhandledState(state);
|
|
646
726
|
}
|
|
647
727
|
} else {
|
|
648
728
|
// If the route was updated with new screen name and/or params, we should navigate there
|
|
649
|
-
action =
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
729
|
+
action = {
|
|
730
|
+
type: 'NAVIGATE',
|
|
731
|
+
payload: {
|
|
732
|
+
name: route.params.screen,
|
|
733
|
+
params: route.params.params,
|
|
734
|
+
path: route.params.path,
|
|
735
|
+
merge: route.params.merge,
|
|
736
|
+
pop: route.params.pop,
|
|
737
|
+
},
|
|
738
|
+
} as const;
|
|
656
739
|
}
|
|
657
740
|
}
|
|
658
741
|
|
|
@@ -665,6 +748,18 @@ export function useNavigationBuilder<
|
|
|
665
748
|
})
|
|
666
749
|
: null;
|
|
667
750
|
|
|
751
|
+
if (
|
|
752
|
+
process.env.NODE_ENV !== 'production' &&
|
|
753
|
+
action != null &&
|
|
754
|
+
updatedState === null
|
|
755
|
+
) {
|
|
756
|
+
const names = routeNames.map((name) => `'${name}'`).join(', ');
|
|
757
|
+
|
|
758
|
+
console.error(
|
|
759
|
+
`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.`
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
|
|
668
763
|
nextState =
|
|
669
764
|
updatedState !== null
|
|
670
765
|
? router.getRehydratedState(updatedState, {
|
|
@@ -675,14 +770,22 @@ export function useNavigationBuilder<
|
|
|
675
770
|
: nextState;
|
|
676
771
|
}
|
|
677
772
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
773
|
+
React.useEffect(() => {
|
|
774
|
+
if (
|
|
775
|
+
consumedParams &&
|
|
776
|
+
didConsumeNestedParams &&
|
|
777
|
+
typeof route?.params === 'object' &&
|
|
778
|
+
route.params != null
|
|
779
|
+
) {
|
|
780
|
+
consumedParams.set(route.params, true);
|
|
781
|
+
}
|
|
782
|
+
}, [consumedParams, didConsumeNestedParams, route?.params]);
|
|
783
|
+
|
|
784
|
+
const shouldUpdate = state !== nextState;
|
|
682
785
|
|
|
683
786
|
useScheduleUpdate(() => {
|
|
684
787
|
if (shouldUpdate) {
|
|
685
|
-
//
|
|
788
|
+
// Schedule an update if the state needs to be updated
|
|
686
789
|
setState(nextState);
|
|
687
790
|
|
|
688
791
|
if (shouldClearUnhandledState) {
|
|
@@ -696,43 +799,59 @@ export function useNavigationBuilder<
|
|
|
696
799
|
// So we override the state object we return to use the latest state as soon as possible
|
|
697
800
|
state = nextState;
|
|
698
801
|
|
|
802
|
+
// Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
|
|
803
|
+
React.useEffect(() => {
|
|
804
|
+
lastStateRef.current = state;
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
const lastNotifiedStateRef = React.useRef<State | null>(null);
|
|
808
|
+
|
|
699
809
|
React.useEffect(() => {
|
|
700
810
|
// In strict mode, React will double-invoke effects.
|
|
701
811
|
// So we need to reset the flag if component was not unmounted
|
|
702
|
-
|
|
812
|
+
stateCleanupRef.current = false;
|
|
703
813
|
|
|
704
814
|
setKey(navigatorKey);
|
|
705
815
|
|
|
706
|
-
if (
|
|
707
|
-
|
|
708
|
-
|
|
816
|
+
if (
|
|
817
|
+
(!getIsInitial() || getIsStateEmitted()) &&
|
|
818
|
+
lastNotifiedStateRef.current !== state
|
|
819
|
+
) {
|
|
820
|
+
// We need to notify the state update in these scenarios:
|
|
821
|
+
// 1. If it's not the initial render of the component containing the navigator
|
|
822
|
+
// 2. If the container has already emitted state before the navigator mounted
|
|
823
|
+
//
|
|
824
|
+
// This will make sure that the container gets notified of state changes due to new mounts
|
|
709
825
|
// This is necessary for proper screen tracking, URL updates etc.
|
|
710
|
-
|
|
826
|
+
//
|
|
827
|
+
// During hydration, a nested navigator may mount after the container's initial state event.
|
|
828
|
+
// Or mount could be delayed due to conditional rendering, suspense etc.
|
|
829
|
+
// So only checking for the initial render is not enough.
|
|
830
|
+
// We also need to propagate the navigator's initial state when it mounts later,
|
|
831
|
+
// so the container is notified that the nested state is now available.
|
|
832
|
+
//
|
|
833
|
+
// We only notify if the state is different from what we already notified
|
|
834
|
+
// Otherwise, this goes into a loop when inside `<Activity mode="hidden">`
|
|
835
|
+
setState(state);
|
|
836
|
+
|
|
837
|
+
lastNotifiedStateRef.current = state;
|
|
711
838
|
}
|
|
712
839
|
|
|
713
840
|
return () => {
|
|
714
841
|
// We need to clean up state for this navigator on unmount
|
|
715
842
|
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
|
716
843
|
setCurrentState(undefined);
|
|
717
|
-
|
|
844
|
+
stateCleanupRef.current = true;
|
|
718
845
|
}
|
|
846
|
+
|
|
847
|
+
// Reset so that StrictMode's second mount re-propagates state correctly.
|
|
848
|
+
// Without this, the guard above sees the same reference and skips setState,
|
|
849
|
+
// causing the initial state to be lost after cleanup wipes the container state.
|
|
850
|
+
lastNotifiedStateRef.current = null;
|
|
719
851
|
};
|
|
720
852
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
721
853
|
}, []);
|
|
722
854
|
|
|
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
855
|
const getState = useLatestCallback((): State => {
|
|
737
856
|
const currentState = getCurrentState();
|
|
738
857
|
|
|
@@ -743,8 +862,6 @@ export function useNavigationBuilder<
|
|
|
743
862
|
);
|
|
744
863
|
});
|
|
745
864
|
|
|
746
|
-
const { onEmitEvent } = React.useContext(NavigationBuilderContext);
|
|
747
|
-
|
|
748
865
|
const emitter = useEventEmitter<EventMapCore<State>>((e) => {
|
|
749
866
|
const routeNames = [];
|
|
750
867
|
|
|
@@ -767,43 +884,48 @@ export function useNavigationBuilder<
|
|
|
767
884
|
return;
|
|
768
885
|
}
|
|
769
886
|
|
|
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);
|
|
887
|
+
const hasPerScreenListeners = routeNames.some(
|
|
888
|
+
(name) => screens[name]?.props.listeners != null
|
|
889
|
+
);
|
|
797
890
|
|
|
798
|
-
|
|
891
|
+
if (screenListeners != null || hasPerScreenListeners) {
|
|
892
|
+
const descriptor = descriptors[route.key];
|
|
799
893
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
894
|
+
if (descriptor == null) {
|
|
895
|
+
throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const navigation = descriptor.navigation;
|
|
899
|
+
|
|
900
|
+
const listeners = ([] as (((e: any) => void) | undefined)[])
|
|
901
|
+
.concat(
|
|
902
|
+
// Get an array of listeners for all screens + common listeners on navigator
|
|
903
|
+
...[
|
|
904
|
+
screenListeners,
|
|
905
|
+
...routeNames.map((name) => {
|
|
906
|
+
const { listeners } = screens[name]?.props ?? {};
|
|
907
|
+
return listeners;
|
|
908
|
+
}),
|
|
909
|
+
].map((listeners) => {
|
|
910
|
+
const map =
|
|
911
|
+
typeof listeners === 'function'
|
|
912
|
+
? listeners({ route: route as any, navigation })
|
|
913
|
+
: listeners;
|
|
914
|
+
|
|
915
|
+
return map
|
|
916
|
+
? Object.keys(map)
|
|
917
|
+
.filter((type) => type === e.type)
|
|
918
|
+
.map((type) => map?.[type])
|
|
919
|
+
: undefined;
|
|
920
|
+
})
|
|
921
|
+
)
|
|
922
|
+
// We don't want same listener to be called multiple times for same event
|
|
923
|
+
// So we remove any duplicate functions from the array
|
|
924
|
+
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
|
925
|
+
|
|
926
|
+
listeners.forEach((listener) => listener?.(e));
|
|
927
|
+
}
|
|
928
|
+
}, onEmitEvent);
|
|
807
929
|
|
|
808
930
|
useFocusEvents({ state, emitter });
|
|
809
931
|
|
|
@@ -837,7 +959,7 @@ export function useNavigationBuilder<
|
|
|
837
959
|
setState,
|
|
838
960
|
});
|
|
839
961
|
|
|
840
|
-
const onUnhandledActionParent = React.
|
|
962
|
+
const onUnhandledActionParent = React.use(UnhandledActionContext);
|
|
841
963
|
|
|
842
964
|
const onUnhandledAction = useLatestCallback((action: NavigationAction) => {
|
|
843
965
|
if (
|
|
@@ -882,9 +1004,9 @@ export function useNavigationBuilder<
|
|
|
882
1004
|
onAction,
|
|
883
1005
|
onUnhandledAction,
|
|
884
1006
|
getState,
|
|
1007
|
+
state,
|
|
885
1008
|
emitter,
|
|
886
1009
|
router,
|
|
887
|
-
stateRef,
|
|
888
1010
|
});
|
|
889
1011
|
|
|
890
1012
|
useFocusedListenersChildrenAdapter({
|
|
@@ -903,9 +1025,7 @@ export function useNavigationBuilder<
|
|
|
903
1025
|
ScreenOptions,
|
|
904
1026
|
EventMap
|
|
905
1027
|
>({
|
|
906
|
-
routes:
|
|
907
|
-
? router.getRoutesFromState(state)
|
|
908
|
-
: state.routes,
|
|
1028
|
+
routes: state.routes,
|
|
909
1029
|
screens,
|
|
910
1030
|
navigation,
|
|
911
1031
|
screenOptions,
|
|
@@ -921,13 +1041,13 @@ export function useNavigationBuilder<
|
|
|
921
1041
|
emitter,
|
|
922
1042
|
});
|
|
923
1043
|
|
|
924
|
-
useCurrentRender({
|
|
925
|
-
state,
|
|
926
|
-
navigation,
|
|
927
|
-
descriptors,
|
|
928
|
-
});
|
|
929
|
-
|
|
930
1044
|
const NavigationContent = useComponent((children: React.ReactNode) => {
|
|
1045
|
+
const focusedRoute = state.routes[state.index];
|
|
1046
|
+
|
|
1047
|
+
if (focusedRoute == null) {
|
|
1048
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
931
1051
|
const element =
|
|
932
1052
|
layout != null
|
|
933
1053
|
? layout({
|
|
@@ -941,8 +1061,13 @@ export function useNavigationBuilder<
|
|
|
941
1061
|
return (
|
|
942
1062
|
<NavigationMetaContext.Provider value={undefined}>
|
|
943
1063
|
<NavigationHelpersContext.Provider value={navigation}>
|
|
944
|
-
<NavigationStateListenerProvider
|
|
945
|
-
|
|
1064
|
+
<NavigationStateListenerProvider
|
|
1065
|
+
state={state}
|
|
1066
|
+
getState={navigation.getState}
|
|
1067
|
+
>
|
|
1068
|
+
<FocusedRouteKeyContext.Provider value={focusedRoute.key}>
|
|
1069
|
+
<PreventRemoveProvider>{element}</PreventRemoveProvider>
|
|
1070
|
+
</FocusedRouteKeyContext.Provider>
|
|
946
1071
|
</NavigationStateListenerProvider>
|
|
947
1072
|
</NavigationHelpersContext.Provider>
|
|
948
1073
|
</NavigationMetaContext.Provider>
|