@react-navigation/core 8.0.0-alpha.34 → 8.0.0-alpha.35
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/README.md +17 -14
- package/lib/module/BaseNavigationContainer.js +14 -2
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/NavigationIndependentTree.js +9 -6
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/NavigationProvider.js +9 -4
- package/lib/module/NavigationProvider.js.map +1 -1
- package/lib/module/PreventRemoveProvider.js +2 -2
- package/lib/module/PreventRemoveProvider.js.map +1 -1
- package/lib/module/SceneView.js +2 -1
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/useDescriptors.js +7 -4
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useFocusEffect.js +4 -2
- package/lib/module/useFocusEffect.js.map +1 -1
- package/lib/module/useFocusEvents.js +4 -2
- package/lib/module/useFocusEvents.js.map +1 -1
- package/lib/module/useIsFocused.js +1 -0
- package/lib/module/useIsFocused.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +40 -37
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +82 -78
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationState.js +48 -23
- package/lib/module/useNavigationState.js.map +1 -1
- package/lib/module/useOptionsGetters.js +18 -17
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/usePreventRemoveContext.js +1 -1
- package/lib/module/usePreventRemoveContext.js.map +1 -1
- package/lib/module/useRouteCache.js +43 -33
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +3 -1
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/module/useSyncState.js +9 -10
- package/lib/module/useSyncState.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
- package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +2 -1
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts +2 -2
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEffect.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
- package/lib/typescript/src/useIsFocused.d.ts +1 -0
- package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +1 -3
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationState.d.ts +1 -0
- package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts +2 -4
- package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts +7 -12
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/src/useSyncState.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/BaseNavigationContainer.tsx +19 -2
- package/src/NavigationBuilderContext.tsx +1 -1
- package/src/NavigationIndependentTree.tsx +16 -8
- package/src/NavigationProvider.tsx +13 -4
- package/src/PreventRemoveProvider.tsx +2 -8
- package/src/SceneView.tsx +3 -1
- package/src/useDescriptors.tsx +18 -5
- package/src/useFocusEffect.tsx +5 -2
- package/src/useFocusEvents.tsx +5 -2
- package/src/useIsFocused.tsx +4 -0
- package/src/useNavigationBuilder.tsx +53 -45
- package/src/useNavigationCache.tsx +127 -105
- package/src/useNavigationState.tsx +60 -29
- package/src/useOptionsGetters.tsx +24 -24
- package/src/usePreventRemoveContext.tsx +1 -1
- package/src/useRouteCache.tsx +49 -33
- package/src/useScheduleUpdate.tsx +4 -1
- package/src/useSyncState.tsx +9 -11
- package/lib/module/useComponent.js +0 -34
- package/lib/module/useComponent.js.map +0 -1
- package/lib/typescript/src/useComponent.d.ts +0 -7
- package/lib/typescript/src/useComponent.d.ts.map +0 -1
- package/src/useComponent.tsx +0 -37
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRouteCache.d.ts","sourceRoot":"","sources":["../../../src/useRouteCache.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"useRouteCache.d.ts","sourceRoot":"","sources":["../../../src/useRouteCache.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAIhF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAS,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,WAAW,eAAwB,CAAC;AAEjD,KAAK,WAAW,GAAG,SAAS,CAAC,aAAa,CAAC,GAAG;IAC5C,CAAC,WAAW,CAAC,CAAC,EAAE,eAAe,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,SAAS,eAAe,EACzD,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,iBAkExB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScheduleUpdate.d.ts","sourceRoot":"","sources":["../../../src/useScheduleUpdate.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useScheduleUpdate.d.ts","sourceRoot":"","sources":["../../../src/useScheduleUpdate.tsx"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,IAAI,QAUrD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSyncState.d.ts","sourceRoot":"","sources":["../../../src/useSyncState.tsx"],"names":[],"mappings":"AAmFA,wBAAgB,YAAY,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC;;;;yCA/BnB,IAAI;wCA4Da,MAAM,IAAI;;
|
|
1
|
+
{"version":3,"file":"useSyncState.d.ts","sourceRoot":"","sources":["../../../src/useSyncState.tsx"],"names":[],"mappings":"AAmFA,wBAAgB,YAAY,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC;;;;yCA/BnB,IAAI;wCA4Da,MAAM,IAAI;;EA+B/D"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-navigation/core",
|
|
3
3
|
"description": "Core utilities for building navigators",
|
|
4
|
-
"version": "8.0.0-alpha.
|
|
4
|
+
"version": "8.0.0-alpha.35",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"react-native",
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
]
|
|
90
90
|
]
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "400f7128965fc92c335a3f6c27e3fa26113915e4"
|
|
93
93
|
}
|
|
@@ -403,10 +403,17 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
|
|
|
403
403
|
}
|
|
404
404
|
);
|
|
405
405
|
|
|
406
|
+
const isOptionsListenerReadyRef = React.useRef(false);
|
|
406
407
|
const lastEmittedOptionsRef = React.useRef<object | undefined>(undefined);
|
|
407
408
|
|
|
408
|
-
const onOptionsChange = useLatestCallback((
|
|
409
|
-
if (
|
|
409
|
+
const onOptionsChange = useLatestCallback(() => {
|
|
410
|
+
if (!isOptionsListenerReadyRef.current) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const options = getCurrentOptions();
|
|
415
|
+
|
|
416
|
+
if (options == null || lastEmittedOptionsRef.current === options) {
|
|
410
417
|
return;
|
|
411
418
|
}
|
|
412
419
|
|
|
@@ -418,6 +425,16 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
|
|
|
418
425
|
});
|
|
419
426
|
});
|
|
420
427
|
|
|
428
|
+
React.useEffect(() => {
|
|
429
|
+
isOptionsListenerReadyRef.current = true;
|
|
430
|
+
|
|
431
|
+
onOptionsChange();
|
|
432
|
+
|
|
433
|
+
return () => {
|
|
434
|
+
isOptionsListenerReadyRef.current = false;
|
|
435
|
+
};
|
|
436
|
+
}, [onOptionsChange]);
|
|
437
|
+
|
|
421
438
|
const lastEmittedStateRef = React.useRef<State>(undefined);
|
|
422
439
|
|
|
423
440
|
const getIsStateEmitted = useLatestCallback(
|
|
@@ -74,7 +74,7 @@ type NavigationBuilderContextValue = {
|
|
|
74
74
|
target: string | undefined;
|
|
75
75
|
data: unknown;
|
|
76
76
|
}) => void;
|
|
77
|
-
onOptionsChange: (
|
|
77
|
+
onOptionsChange: () => void;
|
|
78
78
|
getIsStateEmitted: () => boolean;
|
|
79
79
|
scheduleUpdate: (callback: () => void) => void;
|
|
80
80
|
flushUpdates: () => void;
|
|
@@ -15,7 +15,11 @@ import {
|
|
|
15
15
|
import { NavigationRootContext } from './NavigationRootContext';
|
|
16
16
|
import { PreventRemoveContext } from './PreventRemoveContext';
|
|
17
17
|
import { StaticTreeContext } from './StaticTreeContext';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
FocusedRouteKeyContext,
|
|
20
|
+
IsFocusedContext,
|
|
21
|
+
IsFocusedGetterContext,
|
|
22
|
+
} from './useIsFocused';
|
|
19
23
|
import {
|
|
20
24
|
NamedNavigationStateListenerListContext,
|
|
21
25
|
NavigationStateListenerContext,
|
|
@@ -50,13 +54,17 @@ export function NavigationIndependentTree({
|
|
|
50
54
|
<NavigationRouteContext.Provider value={undefined}>
|
|
51
55
|
<NavigationContext.Provider value={undefined}>
|
|
52
56
|
<IsFocusedContext.Provider value={undefined}>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
<IsFocusedGetterContext.Provider
|
|
58
|
+
value={undefined}
|
|
59
|
+
>
|
|
60
|
+
<IsScreenContext.Provider value={false}>
|
|
61
|
+
<NavigationFocusedRouteStateContext.Provider
|
|
62
|
+
value={undefined}
|
|
63
|
+
>
|
|
64
|
+
{children}
|
|
65
|
+
</NavigationFocusedRouteStateContext.Provider>
|
|
66
|
+
</IsScreenContext.Provider>
|
|
67
|
+
</IsFocusedGetterContext.Provider>
|
|
60
68
|
</IsFocusedContext.Provider>
|
|
61
69
|
</NavigationContext.Provider>
|
|
62
70
|
</NavigationRouteContext.Provider>
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { ParamListBase, Route } from '@react-navigation/routers';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import useLatestCallback from 'use-latest-callback';
|
|
3
4
|
|
|
4
5
|
import type { NavigationProp } from './types';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
FocusedRouteKeyContext,
|
|
8
|
+
IsFocusedContext,
|
|
9
|
+
IsFocusedGetterContext,
|
|
10
|
+
} from './useIsFocused';
|
|
6
11
|
import { useLazyValue } from './useLazyValue';
|
|
7
12
|
import { NamedNavigationStateListenerProvider } from './useNavigationState';
|
|
8
13
|
|
|
@@ -62,6 +67,8 @@ export function NavigationProvider({ route, navigation, children }: Props) {
|
|
|
62
67
|
? focusedRouteKey === route.key
|
|
63
68
|
: false;
|
|
64
69
|
|
|
70
|
+
const getIsFocused = useLatestCallback(() => isFocused);
|
|
71
|
+
|
|
65
72
|
return (
|
|
66
73
|
<NamedNavigationStateListenerProvider name={route.name}>
|
|
67
74
|
<NamedRouteContextListContext.Provider value={NamedRouteContextList}>
|
|
@@ -69,9 +76,11 @@ export function NavigationProvider({ route, navigation, children }: Props) {
|
|
|
69
76
|
<NavigationRouteContext.Provider value={route}>
|
|
70
77
|
<NavigationContext.Provider value={navigation}>
|
|
71
78
|
<IsFocusedContext.Provider value={isFocused}>
|
|
72
|
-
<
|
|
73
|
-
{
|
|
74
|
-
|
|
79
|
+
<IsFocusedGetterContext.Provider value={getIsFocused}>
|
|
80
|
+
<IsScreenContext.Provider value={true}>
|
|
81
|
+
{children}
|
|
82
|
+
</IsScreenContext.Provider>
|
|
83
|
+
</IsFocusedGetterContext.Provider>
|
|
75
84
|
</IsFocusedContext.Provider>
|
|
76
85
|
</NavigationContext.Provider>
|
|
77
86
|
</NavigationRouteContext.Provider>
|
|
@@ -50,15 +50,9 @@ export function PreventRemoveProvider({ children }: Props) {
|
|
|
50
50
|
|
|
51
51
|
const setPreventRemove = useLatestCallback(
|
|
52
52
|
(id: string, routeKey: string, preventRemove: boolean): void => {
|
|
53
|
-
if (
|
|
54
|
-
preventRemove &&
|
|
55
|
-
(navigation == null ||
|
|
56
|
-
navigation
|
|
57
|
-
?.getState()
|
|
58
|
-
.routes.every((route) => route.key !== routeKey))
|
|
59
|
-
) {
|
|
53
|
+
if (preventRemove && navigation == null) {
|
|
60
54
|
throw new Error(
|
|
61
|
-
|
|
55
|
+
"Couldn't find a navigation object. This is likely a bug in the navigator.\n\nIf you're using a custom navigator, make sure that the navigator content is wrapped by the 'render' function returned by 'useNavigationBuilder'."
|
|
62
56
|
);
|
|
63
57
|
}
|
|
64
58
|
|
package/src/SceneView.tsx
CHANGED
|
@@ -22,6 +22,7 @@ type Props<State extends NavigationState, ScreenOptions extends {}> = {
|
|
|
22
22
|
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
|
23
23
|
route: Route<string>;
|
|
24
24
|
routeState: NavigationState | PartialState<NavigationState> | undefined;
|
|
25
|
+
getFocusedRouteKey: () => string | undefined;
|
|
25
26
|
getState: () => State;
|
|
26
27
|
setState: (state: State) => void;
|
|
27
28
|
subscribe: (callback: () => void) => () => void;
|
|
@@ -41,6 +42,7 @@ export function SceneView<
|
|
|
41
42
|
route,
|
|
42
43
|
navigation,
|
|
43
44
|
routeState,
|
|
45
|
+
getFocusedRouteKey,
|
|
44
46
|
getState,
|
|
45
47
|
setState,
|
|
46
48
|
subscribe,
|
|
@@ -53,7 +55,7 @@ export function SceneView<
|
|
|
53
55
|
const { addOptionsGetter } = useOptionsGetters({
|
|
54
56
|
key: route.key,
|
|
55
57
|
options,
|
|
56
|
-
|
|
58
|
+
getFocusedRouteKey,
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
const setKey = React.useCallback((key: string) => {
|
package/src/useDescriptors.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
Router,
|
|
7
7
|
} from '@react-navigation/routers';
|
|
8
8
|
import * as React from 'react';
|
|
9
|
+
import useLatestCallback from 'use-latest-callback';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
type AddKeyedListener,
|
|
@@ -68,11 +69,11 @@ type Options<
|
|
|
68
69
|
ScreenOptions extends {},
|
|
69
70
|
EventMap extends EventMapBase,
|
|
70
71
|
> = {
|
|
71
|
-
routes: State['routes'];
|
|
72
72
|
screens: Record<
|
|
73
73
|
string,
|
|
74
74
|
ScreenConfigWithParent<State, ScreenOptions, EventMap>
|
|
75
75
|
>;
|
|
76
|
+
state: State;
|
|
76
77
|
navigation: NavigationHelpers<ParamListBase>;
|
|
77
78
|
screenOptions: ScreenOptionsOrCallback<ScreenOptions> | undefined;
|
|
78
79
|
screenLayout: ScreenLayout<ScreenOptions> | undefined;
|
|
@@ -101,8 +102,8 @@ export function useDescriptors<
|
|
|
101
102
|
ScreenOptions extends {},
|
|
102
103
|
EventMap extends EventMapBase,
|
|
103
104
|
>({
|
|
104
|
-
routes,
|
|
105
105
|
screens,
|
|
106
|
+
state,
|
|
106
107
|
navigation,
|
|
107
108
|
screenOptions,
|
|
108
109
|
screenLayout,
|
|
@@ -117,6 +118,7 @@ export function useDescriptors<
|
|
|
117
118
|
emitter,
|
|
118
119
|
}: Options<State, ScreenOptions, EventMap>) {
|
|
119
120
|
const theme = React.use(ThemeContext);
|
|
121
|
+
|
|
120
122
|
const [options, setOptions] = React.useState<
|
|
121
123
|
Record<string, Partial<ScreenOptions> | undefined>
|
|
122
124
|
>({});
|
|
@@ -167,7 +169,7 @@ export function useDescriptors<
|
|
|
167
169
|
EventMap,
|
|
168
170
|
ActionHelpers
|
|
169
171
|
>({
|
|
170
|
-
routes,
|
|
172
|
+
routes: state.routes,
|
|
171
173
|
getState,
|
|
172
174
|
navigation,
|
|
173
175
|
setOptions,
|
|
@@ -175,7 +177,11 @@ export function useDescriptors<
|
|
|
175
177
|
emitter,
|
|
176
178
|
});
|
|
177
179
|
|
|
178
|
-
const cachedRoutes = useRouteCache(routes);
|
|
180
|
+
const cachedRoutes = useRouteCache(state.routes);
|
|
181
|
+
|
|
182
|
+
const getFocusedRouteKey = useLatestCallback(
|
|
183
|
+
() => state.routes[state.index]?.key
|
|
184
|
+
);
|
|
179
185
|
|
|
180
186
|
const getOptions = (
|
|
181
187
|
route: RouteProp<ParamListBase, string>,
|
|
@@ -258,6 +264,7 @@ export function useDescriptors<
|
|
|
258
264
|
route={route}
|
|
259
265
|
screen={screen}
|
|
260
266
|
routeState={routeState}
|
|
267
|
+
getFocusedRouteKey={getFocusedRouteKey}
|
|
261
268
|
getState={getState}
|
|
262
269
|
setState={setState}
|
|
263
270
|
subscribe={subscribe}
|
|
@@ -313,7 +320,13 @@ export function useDescriptors<
|
|
|
313
320
|
}
|
|
314
321
|
|
|
315
322
|
const customOptions = getOptions(route, navigation, overrides);
|
|
316
|
-
|
|
323
|
+
|
|
324
|
+
const element = render(
|
|
325
|
+
route,
|
|
326
|
+
navigation,
|
|
327
|
+
customOptions,
|
|
328
|
+
state.routes[i]?.state
|
|
329
|
+
);
|
|
317
330
|
|
|
318
331
|
acc[route.key] = {
|
|
319
332
|
route,
|
package/src/useFocusEffect.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
import { IsFocusedGetterContext } from './useIsFocused';
|
|
3
4
|
import { useNavigation } from './useNavigation';
|
|
4
5
|
|
|
5
6
|
type EffectCallback = () => undefined | void | (() => void);
|
|
@@ -14,6 +15,8 @@ type EffectCallback = () => undefined | void | (() => void);
|
|
|
14
15
|
export function useFocusEffect(effect: EffectCallback) {
|
|
15
16
|
const navigation = useNavigation();
|
|
16
17
|
|
|
18
|
+
const getIsFocused = React.use(IsFocusedGetterContext);
|
|
19
|
+
|
|
17
20
|
// eslint-disable-next-line prefer-rest-params
|
|
18
21
|
if (arguments[1] !== undefined) {
|
|
19
22
|
const message =
|
|
@@ -72,7 +75,7 @@ export function useFocusEffect(effect: EffectCallback) {
|
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
// We need to run the effect on initial render/dep changes if the screen is focused
|
|
75
|
-
if (
|
|
78
|
+
if (getIsFocused?.() ?? true) {
|
|
76
79
|
cleanup = callback();
|
|
77
80
|
isFocused = true;
|
|
78
81
|
}
|
|
@@ -109,5 +112,5 @@ export function useFocusEffect(effect: EffectCallback) {
|
|
|
109
112
|
unsubscribeFocus();
|
|
110
113
|
unsubscribeBlur();
|
|
111
114
|
};
|
|
112
|
-
}, [effect, navigation]);
|
|
115
|
+
}, [effect, navigation, getIsFocused]);
|
|
113
116
|
}
|
package/src/useFocusEvents.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { NavigationContext } from './NavigationProvider';
|
|
5
5
|
import type { EventMapCore } from './types';
|
|
6
6
|
import type { NavigationEventEmitter } from './useEventEmitter';
|
|
7
|
+
import { IsFocusedGetterContext } from './useIsFocused';
|
|
7
8
|
|
|
8
9
|
type Options<State extends NavigationState> = {
|
|
9
10
|
state: State;
|
|
@@ -18,6 +19,8 @@ export function useFocusEvents<State extends NavigationState>({
|
|
|
18
19
|
emitter,
|
|
19
20
|
}: Options<State>) {
|
|
20
21
|
const navigation = React.use(NavigationContext);
|
|
22
|
+
const getIsFocused = React.use(IsFocusedGetterContext);
|
|
23
|
+
|
|
21
24
|
const lastFocusedKeyRef = React.useRef<string | undefined>(undefined);
|
|
22
25
|
|
|
23
26
|
const currentFocusedRoute = state.routes[state.index];
|
|
@@ -51,7 +54,7 @@ export function useFocusEvents<State extends NavigationState>({
|
|
|
51
54
|
React.useEffect(() => {
|
|
52
55
|
const lastFocusedKey = lastFocusedKeyRef.current;
|
|
53
56
|
|
|
54
|
-
const isFocused =
|
|
57
|
+
const isFocused = getIsFocused?.() ?? true;
|
|
55
58
|
|
|
56
59
|
if (isFocused) {
|
|
57
60
|
// Remember the current route as focused if the navigator is focused
|
|
@@ -77,5 +80,5 @@ export function useFocusEvents<State extends NavigationState>({
|
|
|
77
80
|
|
|
78
81
|
emitter.emit({ type: 'blur', target: lastFocusedKey });
|
|
79
82
|
emitter.emit({ type: 'focus', target: currentFocusedKey });
|
|
80
|
-
}, [currentFocusedKey, emitter, navigation]);
|
|
83
|
+
}, [currentFocusedKey, emitter, navigation, getIsFocused]);
|
|
81
84
|
}
|
package/src/useIsFocused.tsx
CHANGED
|
@@ -8,6 +8,10 @@ export const IsFocusedContext = React.createContext<boolean | undefined>(
|
|
|
8
8
|
undefined
|
|
9
9
|
);
|
|
10
10
|
|
|
11
|
+
export const IsFocusedGetterContext = React.createContext<
|
|
12
|
+
(() => boolean) | undefined
|
|
13
|
+
>(undefined);
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* Hook to get the current focus state of the screen. Returns a `true` if screen is focused, otherwise `false`.
|
|
13
17
|
* This can be used if a component needs to render something based on the focus state.
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
} from './types';
|
|
37
37
|
import { UnhandledActionContext } from './UnhandledActionContext';
|
|
38
38
|
import { useChildListeners } from './useChildListeners';
|
|
39
|
-
import { useComponent } from './useComponent';
|
|
40
39
|
import { type ScreenConfigWithParent, useDescriptors } from './useDescriptors';
|
|
41
40
|
import { useEventEmitter } from './useEventEmitter';
|
|
42
41
|
import { useFocusedListenersChildrenAdapter } from './useFocusedListenersChildrenAdapter';
|
|
@@ -462,16 +461,20 @@ export function useNavigationBuilder<
|
|
|
462
461
|
|
|
463
462
|
const { onEmitEvent, getIsStateEmitted } = useNavigationBuilderContext();
|
|
464
463
|
|
|
465
|
-
const
|
|
466
|
-
|
|
464
|
+
const mountStateRef = React.useRef<'initial' | 'mounted' | 'unmounted'>(
|
|
465
|
+
'initial'
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
const previousStateRef = React.useRef(currentState);
|
|
469
|
+
const savedStateRef = React.useRef<State | PartialState<State> | undefined>(
|
|
467
470
|
undefined
|
|
468
471
|
);
|
|
469
472
|
|
|
470
473
|
const setState = useLatestCallback(
|
|
471
474
|
(state: State | PartialState<State> | undefined) => {
|
|
472
|
-
if (
|
|
475
|
+
if (mountStateRef.current === 'unmounted') {
|
|
473
476
|
// Store the state locally in case the current navigator is in `Activity`
|
|
474
|
-
|
|
477
|
+
savedStateRef.current = state;
|
|
475
478
|
|
|
476
479
|
// State might have been already cleaned up due to unmount
|
|
477
480
|
// We don't want to update `route.state` in parent
|
|
@@ -483,6 +486,19 @@ export function useNavigationBuilder<
|
|
|
483
486
|
}
|
|
484
487
|
);
|
|
485
488
|
|
|
489
|
+
const lastState =
|
|
490
|
+
(mountStateRef.current !== 'mounted' ||
|
|
491
|
+
!isStateInitialized(currentState)) &&
|
|
492
|
+
// If the state object differs from previously committed state, we have new state
|
|
493
|
+
// e.g. because of a reset
|
|
494
|
+
// So we need to use it before checking if we should reuse the saved state.
|
|
495
|
+
(currentState === previousStateRef.current ||
|
|
496
|
+
(mountStateRef.current === 'unmounted' && currentState === undefined))
|
|
497
|
+
? // If the navigator is hidden with `Activity`, its state gets cleaned up due to effect cleanup.
|
|
498
|
+
// So we reuse the saved state to avoid remounting screens when the navigator is shown again.
|
|
499
|
+
savedStateRef.current
|
|
500
|
+
: undefined;
|
|
501
|
+
|
|
486
502
|
const [
|
|
487
503
|
stateBeforeInitialization,
|
|
488
504
|
initializedState,
|
|
@@ -494,17 +510,10 @@ export function useNavigationBuilder<
|
|
|
494
510
|
boolean,
|
|
495
511
|
object | undefined,
|
|
496
512
|
] => {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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, {
|
|
513
|
+
if (lastState && isStateValid(lastState)) {
|
|
514
|
+
const state: State = isStateInitialized(lastState)
|
|
515
|
+
? lastState
|
|
516
|
+
: router.getRehydratedState(lastState, {
|
|
508
517
|
routeNames,
|
|
509
518
|
routeParamList,
|
|
510
519
|
routeGetIdList,
|
|
@@ -605,7 +614,7 @@ export function useNavigationBuilder<
|
|
|
605
614
|
// that some changes to routeConfigs are explicitly ignored, such as changes
|
|
606
615
|
// to initialParams
|
|
607
616
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
608
|
-
}, [currentState, router, isStateValid]);
|
|
617
|
+
}, [currentState, lastState, router, isStateValid]);
|
|
609
618
|
|
|
610
619
|
const previousRouteKeyListRef = React.useRef(routeKeyList);
|
|
611
620
|
|
|
@@ -645,7 +654,7 @@ export function useNavigationBuilder<
|
|
|
645
654
|
// If the state isn't initialized, or stale, use the state we initialized instead
|
|
646
655
|
// The state won't update until there's a change needed in the state we have initialized locally
|
|
647
656
|
// So it'll be `undefined` or stale until the first navigation event happens
|
|
648
|
-
isStateInitialized(currentState)
|
|
657
|
+
lastState === undefined && isStateInitialized(currentState)
|
|
649
658
|
? (currentState as State)
|
|
650
659
|
: (initializedState as State);
|
|
651
660
|
|
|
@@ -770,7 +779,7 @@ export function useNavigationBuilder<
|
|
|
770
779
|
: nextState;
|
|
771
780
|
}
|
|
772
781
|
|
|
773
|
-
React.
|
|
782
|
+
React.useInsertionEffect(() => {
|
|
774
783
|
if (
|
|
775
784
|
consumedParams &&
|
|
776
785
|
didConsumeNestedParams &&
|
|
@@ -799,24 +808,29 @@ export function useNavigationBuilder<
|
|
|
799
808
|
// So we override the state object we return to use the latest state as soon as possible
|
|
800
809
|
state = nextState;
|
|
801
810
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
811
|
+
React.useInsertionEffect(() => {
|
|
812
|
+
savedStateRef.current = state;
|
|
813
|
+
previousStateRef.current = currentState;
|
|
805
814
|
});
|
|
806
815
|
|
|
807
|
-
|
|
816
|
+
React.useInsertionEffect(() => {
|
|
817
|
+
return () => {
|
|
818
|
+
// An initially hidden Activity doesn't run passive effects or their cleanup.
|
|
819
|
+
// We mark actual unmount here so queued updates can't write state into a replacement navigator.
|
|
820
|
+
mountStateRef.current = 'unmounted';
|
|
821
|
+
};
|
|
822
|
+
}, []);
|
|
808
823
|
|
|
809
824
|
React.useEffect(() => {
|
|
825
|
+
const savedState = savedStateRef.current ?? state;
|
|
826
|
+
|
|
810
827
|
// In strict mode, React will double-invoke effects.
|
|
811
|
-
// So we need to reset the
|
|
812
|
-
|
|
828
|
+
// So we need to reset the status if component was not unmounted
|
|
829
|
+
mountStateRef.current = 'mounted';
|
|
813
830
|
|
|
814
831
|
setKey(navigatorKey);
|
|
815
832
|
|
|
816
|
-
if (
|
|
817
|
-
(!getIsInitial() || getIsStateEmitted()) &&
|
|
818
|
-
lastNotifiedStateRef.current !== state
|
|
819
|
-
) {
|
|
833
|
+
if (!getIsInitial() || getIsStateEmitted()) {
|
|
820
834
|
// We need to notify the state update in these scenarios:
|
|
821
835
|
// 1. If it's not the initial render of the component containing the navigator
|
|
822
836
|
// 2. If the container has already emitted state before the navigator mounted
|
|
@@ -829,31 +843,25 @@ export function useNavigationBuilder<
|
|
|
829
843
|
// So only checking for the initial render is not enough.
|
|
830
844
|
// We also need to propagate the navigator's initial state when it mounts later,
|
|
831
845
|
// 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;
|
|
846
|
+
setState(savedState);
|
|
838
847
|
}
|
|
839
848
|
|
|
840
849
|
return () => {
|
|
841
850
|
// We need to clean up state for this navigator on unmount
|
|
842
851
|
if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
|
843
852
|
setCurrentState(undefined);
|
|
844
|
-
stateCleanupRef.current = true;
|
|
845
853
|
}
|
|
846
854
|
|
|
847
|
-
|
|
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;
|
|
855
|
+
mountStateRef.current = 'unmounted';
|
|
851
856
|
};
|
|
852
857
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
853
858
|
}, []);
|
|
854
859
|
|
|
855
860
|
const getState = useLatestCallback((): State => {
|
|
856
|
-
const currentState =
|
|
861
|
+
const currentState =
|
|
862
|
+
mountStateRef.current === 'unmounted'
|
|
863
|
+
? savedStateRef.current
|
|
864
|
+
: getCurrentState();
|
|
857
865
|
|
|
858
866
|
return deepFreeze(
|
|
859
867
|
(isStateInitialized(currentState)
|
|
@@ -1026,8 +1034,8 @@ export function useNavigationBuilder<
|
|
|
1026
1034
|
ScreenOptions,
|
|
1027
1035
|
EventMap
|
|
1028
1036
|
>({
|
|
1029
|
-
routes: state.routes,
|
|
1030
1037
|
screens,
|
|
1038
|
+
state,
|
|
1031
1039
|
navigation,
|
|
1032
1040
|
screenOptions,
|
|
1033
1041
|
screenLayout,
|
|
@@ -1043,7 +1051,7 @@ export function useNavigationBuilder<
|
|
|
1043
1051
|
emitter,
|
|
1044
1052
|
});
|
|
1045
1053
|
|
|
1046
|
-
const
|
|
1054
|
+
const render = (children: React.ReactNode) => {
|
|
1047
1055
|
const focusedRoute = state.routes[state.index];
|
|
1048
1056
|
|
|
1049
1057
|
if (focusedRoute == null) {
|
|
@@ -1080,12 +1088,12 @@ export function useNavigationBuilder<
|
|
|
1080
1088
|
</NavigationHelpersContext.Provider>
|
|
1081
1089
|
</NavigationMetaContext.Provider>
|
|
1082
1090
|
);
|
|
1083
|
-
}
|
|
1091
|
+
};
|
|
1084
1092
|
|
|
1085
1093
|
return {
|
|
1086
1094
|
state,
|
|
1087
1095
|
navigation,
|
|
1088
1096
|
descriptors,
|
|
1089
|
-
|
|
1097
|
+
render,
|
|
1090
1098
|
};
|
|
1091
1099
|
}
|