@react-navigation/core 7.21.4 → 7.21.6

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.
Files changed (90) hide show
  1. package/lib/module/BaseNavigationContainer.js +14 -8
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/ConsumedParamsContext.js.map +1 -1
  4. package/lib/module/NavigationIndependentTree.js +8 -4
  5. package/lib/module/NavigationIndependentTree.js.map +1 -1
  6. package/lib/module/SceneView.js +16 -39
  7. package/lib/module/SceneView.js.map +1 -1
  8. package/lib/module/StaticNavigation.js +1 -1
  9. package/lib/module/StaticNavigation.js.map +1 -1
  10. package/lib/module/findFocusedRoute.js +3 -3
  11. package/lib/module/findFocusedRoute.js.map +1 -1
  12. package/lib/module/getActionFromState.js.map +1 -1
  13. package/lib/module/getPathFromState.js +75 -45
  14. package/lib/module/getPathFromState.js.map +1 -1
  15. package/lib/module/getPatternParts.js +15 -2
  16. package/lib/module/getPatternParts.js.map +1 -1
  17. package/lib/module/getStateFromPath.js +47 -21
  18. package/lib/module/getStateFromPath.js.map +1 -1
  19. package/lib/module/getStateFromRouteParams.js +24 -0
  20. package/lib/module/getStateFromRouteParams.js.map +1 -0
  21. package/lib/module/useFocusEvents.js +6 -2
  22. package/lib/module/useFocusEvents.js.map +1 -1
  23. package/lib/module/useLazyValue.js.map +1 -1
  24. package/lib/module/useNavigationBuilder.js +9 -6
  25. package/lib/module/useNavigationBuilder.js.map +1 -1
  26. package/lib/module/useNavigationState.js +44 -11
  27. package/lib/module/useNavigationState.js.map +1 -1
  28. package/lib/module/useOnPreventRemove.js +1 -3
  29. package/lib/module/useOnPreventRemove.js.map +1 -1
  30. package/lib/module/useRouteCache.js +1 -3
  31. package/lib/module/useRouteCache.js.map +1 -1
  32. package/lib/module/useSyncState.js +3 -2
  33. package/lib/module/useSyncState.js.map +1 -1
  34. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  35. package/lib/typescript/src/ConsumedParamsContext.d.ts +1 -6
  36. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -1
  37. package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
  38. package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
  39. package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
  40. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  41. package/lib/typescript/src/NavigationProvider.d.ts +1 -1
  42. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  43. package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
  44. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  45. package/lib/typescript/src/SceneView.d.ts +2 -1
  46. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  47. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  48. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  49. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  50. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  51. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  52. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  53. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  54. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  55. package/lib/typescript/src/theming/ThemeProvider.d.ts +1 -1
  56. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
  57. package/lib/typescript/src/types.d.ts +15 -9
  58. package/lib/typescript/src/types.d.ts.map +1 -1
  59. package/lib/typescript/src/useComponent.d.ts +1 -1
  60. package/lib/typescript/src/useComponent.d.ts.map +1 -1
  61. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  62. package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
  63. package/lib/typescript/src/useNavigationBuilder.d.ts +1 -1
  64. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  65. package/lib/typescript/src/useNavigationState.d.ts +3 -2
  66. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  67. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  68. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  69. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  70. package/package.json +7 -7
  71. package/src/BaseNavigationContainer.tsx +16 -10
  72. package/src/ConsumedParamsContext.tsx +1 -6
  73. package/src/NavigationIndependentTree.tsx +8 -5
  74. package/src/SceneView.tsx +18 -51
  75. package/src/StaticNavigation.tsx +2 -2
  76. package/src/findFocusedRoute.tsx +5 -3
  77. package/src/getActionFromState.tsx +3 -1
  78. package/src/getPathFromState.tsx +130 -59
  79. package/src/getPatternParts.tsx +15 -2
  80. package/src/getStateFromPath.tsx +76 -17
  81. package/src/getStateFromRouteParams.tsx +60 -0
  82. package/src/types.tsx +42 -24
  83. package/src/useDescriptors.tsx +1 -1
  84. package/src/useFocusEvents.tsx +7 -5
  85. package/src/useLazyValue.tsx +2 -2
  86. package/src/useNavigationBuilder.tsx +16 -8
  87. package/src/useNavigationState.tsx +51 -21
  88. package/src/useOnPreventRemove.tsx +4 -3
  89. package/src/useRouteCache.tsx +1 -4
  90. package/src/useSyncState.tsx +3 -2
package/src/types.tsx CHANGED
@@ -190,7 +190,7 @@ export type DefaultNavigatorOptions<
190
190
  ScreenOptions,
191
191
  EventMap
192
192
  >,
193
- RouteProp<ParamList>
193
+ ScreenRouteProp<ParamList>
194
194
  >
195
195
  >;
196
196
  children: React.ReactNode;
@@ -202,7 +202,7 @@ export type DefaultNavigatorOptions<
202
202
  screenListeners?:
203
203
  | ScreenListeners<State, EventMap>
204
204
  | ((props: {
205
- route: RouteProp<ParamList>;
205
+ route: ScreenRouteProp<ParamList>;
206
206
  navigation: Navigation;
207
207
  }) => ScreenListeners<State, EventMap>);
208
208
 
@@ -212,7 +212,7 @@ export type DefaultNavigatorOptions<
212
212
  screenOptions?:
213
213
  | ScreenOptions
214
214
  | ((props: {
215
- route: RouteProp<ParamList>;
215
+ route: ScreenRouteProp<ParamList>;
216
216
  navigation: Navigation;
217
217
  theme: ReactNavigation.Theme;
218
218
  }) => ScreenOptions);
@@ -647,6 +647,13 @@ export type RouteProp<
647
647
  RouteName extends keyof ParamList = Keyof<ParamList>,
648
648
  > = Route<Extract<RouteName, string>, ParamList[RouteName]>;
649
649
 
650
+ type ScreenRouteProp<
651
+ ParamList extends ParamListBase,
652
+ RouteName extends keyof ParamList = Keyof<ParamList>,
653
+ > = {
654
+ [Name in Extract<RouteName, string>]: RouteProp<ParamList, Name>;
655
+ }[Extract<RouteName, string>];
656
+
650
657
  export type CompositeNavigationProp<
651
658
  A extends NavigationProp<ParamListBase, string, any, any, any>,
652
659
  B extends NavigationHelpersCommon<ParamListBase, any>,
@@ -709,7 +716,7 @@ export type ScreenLayoutArgs<
709
716
  ScreenOptions extends {},
710
717
  Navigation,
711
718
  > = {
712
- route: RouteProp<ParamList, RouteName>;
719
+ route: ScreenRouteProp<ParamList, RouteName>;
713
720
  options: ScreenOptions;
714
721
  navigation: Navigation;
715
722
  theme: ReactNavigation.Theme;
@@ -896,7 +903,7 @@ export type RouteGroupConfig<
896
903
  screenOptions?:
897
904
  | ScreenOptions
898
905
  | ((props: {
899
- route: RouteProp<ParamList, keyof ParamList>;
906
+ route: ScreenRouteProp<ParamList>;
900
907
  navigation: Navigation;
901
908
  theme: ReactNavigation.Theme;
902
909
  }) => ScreenOptions);
@@ -1103,25 +1110,36 @@ export type NavigatorTypeBag<
1103
1110
  Navigator: Navigator;
1104
1111
  };
1105
1112
 
1106
- type TypedNavigatorStaticComponent<Bag extends NavigatorTypeBagBase> =
1107
- React.ComponentType<
1108
- Partial<
1113
+ type StaticParamListForConfig<Config> =
1114
+ Config extends StaticNavigationConfig['config']
1115
+ ? StaticParamList<{ config: Config }> extends infer ParamList extends
1116
+ ParamListBase
1117
+ ? ParamList
1118
+ : ParamListBase
1119
+ : ParamListBase;
1120
+
1121
+ type TypedNavigatorStaticComponent<
1122
+ Bag extends NavigatorTypeBagBase,
1123
+ Config,
1124
+ > = React.ComponentType<
1125
+ Partial<
1126
+ Omit<
1109
1127
  Omit<
1110
- React.ComponentProps<
1111
- TypedNavigatorInternal<
1112
- Bag['ParamList'],
1113
- Bag['NavigatorID'],
1114
- Bag['State'],
1115
- Bag['ScreenOptions'],
1116
- Bag['EventMap'],
1117
- Bag['NavigationList'],
1118
- Bag['Navigator']
1119
- >['Navigator']
1128
+ React.ComponentProps<Bag['Navigator']>,
1129
+ keyof DefaultNavigatorOptions<any, any, any, any, any, any>
1130
+ > &
1131
+ DefaultNavigatorOptions<
1132
+ StaticParamListForConfig<Config>,
1133
+ Bag['NavigatorID'],
1134
+ Bag['State'],
1135
+ Bag['ScreenOptions'],
1136
+ Bag['EventMap'],
1137
+ Bag['NavigationList'][keyof Bag['ParamList']]
1120
1138
  >,
1121
- 'children'
1122
- >
1139
+ 'children'
1123
1140
  >
1124
- >;
1141
+ >
1142
+ >;
1125
1143
 
1126
1144
  type TypedNavigatorStaticDecorated<
1127
1145
  Bag extends NavigatorTypeBagBase,
@@ -1154,10 +1172,10 @@ type TypedNavigatorStatic<
1154
1172
  config: Config;
1155
1173
  with: (
1156
1174
  Component: React.ComponentType<{
1157
- Navigator: TypedNavigatorStaticComponent<Bag>;
1175
+ Navigator: TypedNavigatorStaticComponent<Bag, Config>;
1158
1176
  }>
1159
1177
  ) => TypedNavigatorStaticDecorated<Bag, Config>;
1160
- getComponent: () => TypedNavigatorStaticComponent<Bag>;
1178
+ getComponent: () => TypedNavigatorStaticComponent<Bag, Config>;
1161
1179
  } & PrivateValueStore<[Bag['ParamList'], Bag['NavigationList'], unknown]>;
1162
1180
 
1163
1181
  export type TypedNavigator<
@@ -1284,7 +1302,7 @@ type PathConfigAlias = {
1284
1302
  export type PathConfig<ParamList extends {}> = Partial<PathConfigAlias> & {
1285
1303
  /**
1286
1304
  * An object mapping the param name to a function which converts the param value to a string.
1287
- * By default, all params are converted to strings using `String(value)`.
1305
+ * By default, arrays and null are preserved for query params, and other values are converted using `String(value)`.
1288
1306
  *
1289
1307
  * @example
1290
1308
  * ```js
@@ -311,7 +311,7 @@ export function useDescriptors<
311
311
  render() {
312
312
  return element;
313
313
  },
314
- options: customOptions as ScreenOptions,
314
+ options: customOptions,
315
315
  };
316
316
 
317
317
  return acc;
@@ -45,7 +45,12 @@ export function useFocusEvents<State extends NavigationState>({
45
45
  React.useEffect(() => {
46
46
  const lastFocusedKey = lastFocusedKeyRef.current;
47
47
 
48
- lastFocusedKeyRef.current = currentFocusedKey;
48
+ const isFocused = navigation ? navigation.isFocused() : true;
49
+
50
+ if (isFocused) {
51
+ // Remember the current route as focused if the navigator is focused
52
+ lastFocusedKeyRef.current = currentFocusedKey;
53
+ }
49
54
 
50
55
  // We wouldn't have `lastFocusedKey` on initial mount
51
56
  // Fire focus event for the current route on mount if there's no parent navigator
@@ -55,10 +60,7 @@ export function useFocusEvents<State extends NavigationState>({
55
60
 
56
61
  // We should only emit events when the focused key changed and navigator is focused
57
62
  // When navigator is not focused, screens inside shouldn't receive focused status either
58
- if (
59
- lastFocusedKey === currentFocusedKey ||
60
- !(navigation ? navigation.isFocused() : true)
61
- ) {
63
+ if (lastFocusedKey === currentFocusedKey || !isFocused) {
62
64
  return;
63
65
  }
64
66
 
@@ -1,11 +1,11 @@
1
1
  import * as React from 'react';
2
2
 
3
- export function useLazyValue<T>(create: () => T) {
3
+ export function useLazyValue<T>(create: () => T): T {
4
4
  const lazyRef = React.useRef<T>(undefined);
5
5
 
6
6
  if (lazyRef.current === undefined) {
7
7
  lazyRef.current = create();
8
8
  }
9
9
 
10
- return lazyRef.current as T;
10
+ return lazyRef.current;
11
11
  }
@@ -341,7 +341,7 @@ export function useNavigationBuilder<
341
341
 
342
342
  const isNestedParamsConsumed =
343
343
  typeof route?.params === 'object' && route.params != null
344
- ? consumedParams?.isConsumed(route.params)
344
+ ? consumedParams?.has(route.params)
345
345
  : false;
346
346
 
347
347
  const {
@@ -575,7 +575,14 @@ export function useNavigationBuilder<
575
575
  return [stateBeforeInitialization, hydratedState, true, paramsForState];
576
576
  }
577
577
 
578
- return [undefined, hydratedState, false, paramsForState];
578
+ return [
579
+ undefined,
580
+ hydratedState,
581
+ false,
582
+ // Current state is already initialized
583
+ // So params weren't used, and need to be handled later
584
+ isStateInitialized(currentState) ? undefined : paramsForState,
585
+ ];
579
586
  }
580
587
  // We explicitly don't include routeNames, route.params etc. in the dep list
581
588
  // below. We want to avoid forcing a new state to be calculated in those cases
@@ -723,18 +730,16 @@ export function useNavigationBuilder<
723
730
  : nextState;
724
731
  }
725
732
 
726
- const setConsumedParams = consumedParams?.setConsumed;
727
-
728
733
  React.useEffect(() => {
729
734
  if (
730
- setConsumedParams &&
735
+ consumedParams &&
731
736
  didConsumeNestedParams &&
732
737
  typeof route?.params === 'object' &&
733
738
  route.params != null
734
739
  ) {
735
- setConsumedParams(route.params);
740
+ consumedParams.set(route.params, true);
736
741
  }
737
- }, [didConsumeNestedParams, route?.params, setConsumedParams]);
742
+ }, [consumedParams, didConsumeNestedParams, route?.params]);
738
743
 
739
744
  const shouldUpdate = state !== nextState;
740
745
 
@@ -998,7 +1003,10 @@ export function useNavigationBuilder<
998
1003
  return (
999
1004
  <NavigationMetaContext.Provider value={undefined}>
1000
1005
  <NavigationHelpersContext.Provider value={navigation}>
1001
- <NavigationStateListenerProvider state={state}>
1006
+ <NavigationStateListenerProvider
1007
+ state={state}
1008
+ getState={navigation.getState}
1009
+ >
1002
1010
  <FocusedRouteKeyContext.Provider
1003
1011
  value={state.routes[state.index].key}
1004
1012
  >
@@ -1,7 +1,13 @@
1
1
  import type { NavigationState, ParamListBase } from '@react-navigation/routers';
2
2
  import * as React from 'react';
3
3
  import useLatestCallback from 'use-latest-callback';
4
- import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector';
4
+
5
+ import { useClientLayoutEffect } from './useClientLayoutEffect';
6
+
7
+ type NavigationStateListener = {
8
+ getState: () => NavigationState<ParamListBase>;
9
+ subscribe: (callback: () => void) => () => void;
10
+ };
5
11
 
6
12
  type Selector<ParamList extends ParamListBase, T> = (
7
13
  state: NavigationState<ParamList>
@@ -15,36 +21,64 @@ type Selector<ParamList extends ParamListBase, T> = (
15
21
  export function useNavigationState<ParamList extends ParamListBase, T>(
16
22
  selector: Selector<ParamList, T>
17
23
  ): T {
18
- const stateListener = React.useContext(NavigationStateListenerContext);
24
+ if (typeof selector !== 'function') {
25
+ throw new Error(
26
+ `A selector function must be provided (got ${typeof selector}).`
27
+ );
28
+ }
19
29
 
20
- if (stateListener == null) {
30
+ const listener = React.useContext(NavigationStateListenerContext);
31
+
32
+ if (listener == null) {
21
33
  throw new Error(
22
34
  "Couldn't get the navigation state. Is your component inside a navigator?"
23
35
  );
24
36
  }
25
37
 
26
- const value = useSyncExternalStoreWithSelector(
27
- stateListener.subscribe,
28
- // @ts-expect-error: this is unsafe, but needed to make the generic work
29
- stateListener.getState,
30
- stateListener.getState,
31
- selector
32
- );
38
+ const { getState, subscribe } = listener;
39
+
40
+ const [, forceUpdate] = React.useReducer((count: number) => count + 1, 0);
41
+
42
+ // @ts-expect-error: this is unsafe, but we need to support it for now
43
+ const selected = selector(getState());
44
+
45
+ const selectionRef = React.useRef({ select: selector, selected });
33
46
 
34
- return value;
47
+ useClientLayoutEffect(() => {
48
+ selectionRef.current = { select: selector, selected };
49
+ });
50
+
51
+ React.useEffect(() => {
52
+ const checkForUpdates = () => {
53
+ const selection = selectionRef.current;
54
+
55
+ // @ts-expect-error: this is unsafe, but we need to support it for now
56
+ if (!Object.is(selection.selected, selection.select(getState()))) {
57
+ forceUpdate();
58
+ }
59
+ };
60
+
61
+ const unsubscribe = subscribe(checkForUpdates);
62
+
63
+ // The state may have changed between the render and the subscription
64
+ checkForUpdates();
65
+
66
+ return unsubscribe;
67
+ }, [getState, subscribe]);
68
+
69
+ return selected;
35
70
  }
36
71
 
37
72
  export function NavigationStateListenerProvider({
38
73
  state,
74
+ getState,
39
75
  children,
40
76
  }: {
41
77
  state: NavigationState<ParamListBase>;
78
+ getState: () => NavigationState<ParamListBase>;
42
79
  children: React.ReactNode;
43
80
  }) {
44
81
  const listeners = React.useRef<(() => void)[]>([]);
45
- const stateRef = React.useRef(state);
46
-
47
- const getState = useLatestCallback(() => stateRef.current);
48
82
 
49
83
  const subscribe = useLatestCallback((callback: () => void) => {
50
84
  listeners.current.push(callback);
@@ -54,8 +88,8 @@ export function NavigationStateListenerProvider({
54
88
  };
55
89
  });
56
90
 
57
- React.useInsertionEffect(() => {
58
- stateRef.current = state;
91
+ // Notify subscribers once the new state has committed
92
+ useClientLayoutEffect(() => {
59
93
  listeners.current.forEach((callback) => callback());
60
94
  }, [state]);
61
95
 
@@ -75,9 +109,5 @@ export function NavigationStateListenerProvider({
75
109
  }
76
110
 
77
111
  const NavigationStateListenerContext = React.createContext<
78
- | {
79
- getState: () => NavigationState<ParamListBase>;
80
- subscribe: (callback: () => void) => () => void;
81
- }
82
- | undefined
112
+ NavigationStateListener | undefined
83
113
  >(undefined);
@@ -34,9 +34,10 @@ export const shouldPreventRemove = (
34
34
  .filter((route) => !nextRouteKeys.includes(route.key))
35
35
  .reverse();
36
36
 
37
- const visitedRouteKeys: Set<string> =
38
- // @ts-expect-error: add this property to mark that we've already emitted this action
39
- action[VISITED_ROUTE_KEYS] ?? new Set<string>();
37
+ const visitedRouteKeys =
38
+ VISITED_ROUTE_KEYS in action && action[VISITED_ROUTE_KEYS] instanceof Set
39
+ ? action[VISITED_ROUTE_KEYS]
40
+ : new Set<string>();
40
41
 
41
42
  const beforeRemoveAction = {
42
43
  ...action,
@@ -41,10 +41,7 @@ export function useRouteCache<State extends NavigationState>(
41
41
  // FIXME: since the state is updated with mutation, the route object cannot be frozen
42
42
  // As a workaround, loop through the object and make the properties readonly
43
43
  // Only needed once per proxy - skip if we're reusing a previously-frozen one
44
- for (const key in proxy) {
45
- // @ts-expect-error: this is fine since we are looping through the object
46
- const value = proxy[key];
47
-
44
+ for (const [key, value] of Object.entries(proxy)) {
48
45
  Object.defineProperty(proxy, key, {
49
46
  enumerable: true,
50
47
  configurable: true,
@@ -25,9 +25,10 @@ const createStore = <T,>(getInitialState: () => T) => {
25
25
 
26
26
  const setState = (newState: T) => {
27
27
  state = deepFreeze(newState);
28
- didUpdate = true;
29
28
 
30
- if (!isBatching) {
29
+ if (isBatching) {
30
+ didUpdate = true;
31
+ } else {
31
32
  listeners.forEach((listener) => listener());
32
33
  }
33
34
  };