@react-navigation/core 7.0.2 → 7.0.3

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 (49) hide show
  1. package/lib/commonjs/BaseNavigationContainer.js +3 -2
  2. package/lib/commonjs/BaseNavigationContainer.js.map +1 -1
  3. package/lib/commonjs/NavigationBuilderContext.js +4 -1
  4. package/lib/commonjs/NavigationBuilderContext.js.map +1 -1
  5. package/lib/commonjs/SceneView.js +29 -4
  6. package/lib/commonjs/SceneView.js.map +1 -1
  7. package/lib/commonjs/useDescriptors.js +3 -1
  8. package/lib/commonjs/useDescriptors.js.map +1 -1
  9. package/lib/commonjs/useNavigationBuilder.js +8 -1
  10. package/lib/commonjs/useNavigationBuilder.js.map +1 -1
  11. package/lib/commonjs/useSyncState.js +41 -2
  12. package/lib/commonjs/useSyncState.js.map +1 -1
  13. package/lib/module/BaseNavigationContainer.js +3 -2
  14. package/lib/module/BaseNavigationContainer.js.map +1 -1
  15. package/lib/module/NavigationBuilderContext.js +4 -1
  16. package/lib/module/NavigationBuilderContext.js.map +1 -1
  17. package/lib/module/SceneView.js +29 -4
  18. package/lib/module/SceneView.js.map +1 -1
  19. package/lib/module/useDescriptors.js +3 -1
  20. package/lib/module/useDescriptors.js.map +1 -1
  21. package/lib/module/useNavigationBuilder.js +8 -1
  22. package/lib/module/useNavigationBuilder.js.map +1 -1
  23. package/lib/module/useSyncState.js +40 -2
  24. package/lib/module/useSyncState.js.map +1 -1
  25. package/lib/typescript/commonjs/src/BaseNavigationContainer.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/src/NavigationBuilderContext.d.ts +1 -0
  27. package/lib/typescript/commonjs/src/NavigationBuilderContext.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/src/SceneView.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/src/useDescriptors.d.ts.map +1 -1
  30. package/lib/typescript/commonjs/src/useNavigationBuilder.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/src/useSyncState.d.ts +1 -1
  32. package/lib/typescript/commonjs/src/useSyncState.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/tsconfig.build.tsbuildinfo +1 -1
  34. package/lib/typescript/module/src/BaseNavigationContainer.d.ts.map +1 -1
  35. package/lib/typescript/module/src/NavigationBuilderContext.d.ts +1 -0
  36. package/lib/typescript/module/src/NavigationBuilderContext.d.ts.map +1 -1
  37. package/lib/typescript/module/src/SceneView.d.ts.map +1 -1
  38. package/lib/typescript/module/src/useDescriptors.d.ts.map +1 -1
  39. package/lib/typescript/module/src/useNavigationBuilder.d.ts.map +1 -1
  40. package/lib/typescript/module/src/useSyncState.d.ts +1 -1
  41. package/lib/typescript/module/src/useSyncState.d.ts.map +1 -1
  42. package/lib/typescript/module/tsconfig.build.tsbuildinfo +1 -1
  43. package/package.json +2 -2
  44. package/src/BaseNavigationContainer.tsx +10 -3
  45. package/src/NavigationBuilderContext.tsx +4 -0
  46. package/src/SceneView.tsx +31 -3
  47. package/src/useDescriptors.tsx +4 -3
  48. package/src/useNavigationBuilder.tsx +7 -1
  49. package/src/useSyncState.tsx +49 -2
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": "7.0.2",
4
+ "version": "7.0.3",
5
5
  "keywords": [
6
6
  "react",
7
7
  "react-native",
@@ -95,5 +95,5 @@
95
95
  ]
96
96
  ]
97
97
  },
98
- "gitHead": "987f0a874506c76c62dfd4335551fd2d0b0b52d1"
98
+ "gitHead": "596ce0641241b9f003a6ad130f6f4837206ef064"
99
99
  }
@@ -103,8 +103,8 @@ export const BaseNavigationContainer = React.forwardRef(
103
103
  );
104
104
  }
105
105
 
106
- const [state, getState, setState] = useSyncState<State>(() =>
107
- getPartialState(initialState == null ? undefined : initialState)
106
+ const [state, getState, setState, scheduleUpdate] = useSyncState<State>(
107
+ () => getPartialState(initialState == null ? undefined : initialState)
108
108
  );
109
109
 
110
110
  const isFirstMountRef = React.useRef<boolean>(true);
@@ -260,9 +260,16 @@ export const BaseNavigationContainer = React.forwardRef(
260
260
  addKeyedListener,
261
261
  onDispatchAction,
262
262
  onOptionsChange,
263
+ scheduleUpdate,
263
264
  stackRef,
264
265
  }),
265
- [addListener, addKeyedListener, onDispatchAction, onOptionsChange]
266
+ [
267
+ addListener,
268
+ addKeyedListener,
269
+ onDispatchAction,
270
+ onOptionsChange,
271
+ scheduleUpdate,
272
+ ]
266
273
  );
267
274
 
268
275
  const isInitialRef = React.useRef(true);
@@ -61,8 +61,12 @@ export const NavigationBuilderContext = React.createContext<{
61
61
  onRouteFocus?: (key: string) => void;
62
62
  onDispatchAction: (action: NavigationAction, noop: boolean) => void;
63
63
  onOptionsChange: (options: object) => void;
64
+ scheduleUpdate: (callback: () => void) => void;
64
65
  stackRef?: React.MutableRefObject<string | undefined>;
65
66
  }>({
66
67
  onDispatchAction: () => undefined,
67
68
  onOptionsChange: () => undefined,
69
+ scheduleUpdate: () => {
70
+ throw new Error("Couldn't find a context for scheduling updates.");
71
+ },
68
72
  });
package/src/SceneView.tsx CHANGED
@@ -72,9 +72,37 @@ export function SceneView<
72
72
 
73
73
  setState({
74
74
  ...state,
75
- routes: state.routes.map((r) =>
76
- r.key === route.key ? { ...r, state: child } : r
77
- ),
75
+ routes: state.routes.map((r) => {
76
+ if (r.key !== route.key) {
77
+ return r;
78
+ }
79
+
80
+ const nextRoute = { ...r, state: child };
81
+
82
+ // Before updating the state, cleanup any nested screen and state
83
+ // This will avoid the navigator trying to handle them again
84
+ if (
85
+ nextRoute.params &&
86
+ (('state' in nextRoute.params &&
87
+ typeof nextRoute.params.state === 'object' &&
88
+ nextRoute.params.state !== null) ||
89
+ ('screen' in nextRoute.params &&
90
+ typeof nextRoute.params.screen === 'string'))
91
+ ) {
92
+ // @ts-expect-error: we don't have correct type for params
93
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
94
+ const { state, screen, params, initial, ...rest } =
95
+ nextRoute.params;
96
+
97
+ if (Object.keys(rest).length) {
98
+ nextRoute.params = rest;
99
+ } else {
100
+ delete nextRoute.params;
101
+ }
102
+ }
103
+
104
+ return nextRoute;
105
+ }),
78
106
  });
79
107
  },
80
108
  [getState, route.key, setState]
@@ -116,9 +116,8 @@ export function useDescriptors<
116
116
  const [options, setOptions] = React.useState<Record<string, ScreenOptions>>(
117
117
  {}
118
118
  );
119
- const { onDispatchAction, onOptionsChange, stackRef } = React.useContext(
120
- NavigationBuilderContext
121
- );
119
+ const { onDispatchAction, onOptionsChange, scheduleUpdate, stackRef } =
120
+ React.useContext(NavigationBuilderContext);
122
121
 
123
122
  const context = React.useMemo(
124
123
  () => ({
@@ -129,6 +128,7 @@ export function useDescriptors<
129
128
  onRouteFocus,
130
129
  onDispatchAction,
131
130
  onOptionsChange,
131
+ scheduleUpdate,
132
132
  stackRef,
133
133
  }),
134
134
  [
@@ -139,6 +139,7 @@ export function useDescriptors<
139
139
  onRouteFocus,
140
140
  onDispatchAction,
141
141
  onOptionsChange,
142
+ scheduleUpdate,
142
143
  stackRef,
143
144
  ]
144
145
  );
@@ -18,6 +18,7 @@ import { deepFreeze } from './deepFreeze';
18
18
  import { Group } from './Group';
19
19
  import { isArrayEqual } from './isArrayEqual';
20
20
  import { isRecordEqual } from './isRecordEqual';
21
+ import { NavigationBuilderContext } from './NavigationBuilderContext';
21
22
  import { NavigationHelpersContext } from './NavigationHelpersContext';
22
23
  import { NavigationRouteContext } from './NavigationRouteContext';
23
24
  import { NavigationStateContext } from './NavigationStateContext';
@@ -559,12 +560,17 @@ export function useNavigationBuilder<
559
560
  : nextState;
560
561
  }
561
562
 
563
+ const { scheduleUpdate } = React.useContext(NavigationBuilderContext);
564
+
562
565
  const shouldUpdate = state !== nextState;
563
566
 
564
567
  useIsomorphicLayoutEffect(() => {
565
568
  if (shouldUpdate) {
566
569
  // If the state needs to be updated, we'll schedule an update
567
- setState(nextState);
570
+ // These updates will be batched and run in the reverse order
571
+ scheduleUpdate(() => {
572
+ setState(nextState);
573
+ });
568
574
  }
569
575
  });
570
576
 
@@ -1,6 +1,8 @@
1
1
  import * as React from 'react';
2
+ import useLatestCallback from 'use-latest-callback';
2
3
 
3
4
  import { deepFreeze } from './deepFreeze';
5
+ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
4
6
 
5
7
  const createStore = <T,>(getInitialState: () => T) => {
6
8
  const listeners: (() => void)[] = [];
@@ -19,10 +21,16 @@ const createStore = <T,>(getInitialState: () => T) => {
19
21
  return state;
20
22
  };
21
23
 
24
+ let isBatching = false;
25
+ let didUpdate = false;
26
+
22
27
  const setState = (newState: T) => {
23
28
  state = deepFreeze(newState);
29
+ didUpdate = true;
24
30
 
25
- listeners.forEach((listener) => listener());
31
+ if (!isBatching) {
32
+ listeners.forEach((listener) => listener());
33
+ }
26
34
  };
27
35
 
28
36
  const subscribe = (callback: () => void) => {
@@ -37,9 +45,21 @@ const createStore = <T,>(getInitialState: () => T) => {
37
45
  };
38
46
  };
39
47
 
48
+ const batchUpdates = (callback: () => void) => {
49
+ isBatching = true;
50
+ callback();
51
+ isBatching = false;
52
+
53
+ if (didUpdate) {
54
+ didUpdate = false;
55
+ listeners.forEach((listener) => listener());
56
+ }
57
+ };
58
+
40
59
  return {
41
60
  getState,
42
61
  setState,
62
+ batchUpdates,
43
63
  subscribe,
44
64
  };
45
65
  };
@@ -55,5 +75,32 @@ export function useSyncState<T>(getInitialState: () => T) {
55
75
 
56
76
  React.useDebugValue(state);
57
77
 
58
- return [state, store.getState, store.setState] as const;
78
+ const pendingUpdatesRef = React.useRef<(() => void)[]>([]);
79
+
80
+ const scheduleUpdate = useLatestCallback((callback: () => void) => {
81
+ pendingUpdatesRef.current.push(callback);
82
+ });
83
+
84
+ useIsomorphicLayoutEffect(() => {
85
+ // Flush all the pending updates
86
+ const pendingUpdates = pendingUpdatesRef.current;
87
+
88
+ pendingUpdatesRef.current = [];
89
+
90
+ if (pendingUpdates.length !== 0) {
91
+ store.batchUpdates(() => {
92
+ // Flush all the pending updates
93
+ // These updates should be scheduled in useEffect
94
+ // Run them in reverse order so that the deepest updates are run last
95
+ // This is opposite to useEffect where the deepest effects are run first
96
+ for (let i = pendingUpdates.length - 1; i >= 0; i--) {
97
+ const update = pendingUpdates[i];
98
+
99
+ update();
100
+ }
101
+ });
102
+ }
103
+ });
104
+
105
+ return [state, store.getState, store.setState, scheduleUpdate] as const;
59
106
  }