@react-navigation/core 8.0.0-alpha.30 → 8.0.0-alpha.32

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 (123) hide show
  1. package/lib/module/BaseNavigationContainer.js +132 -82
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/DataLoading.js +3 -1
  4. package/lib/module/DataLoading.js.map +1 -1
  5. package/lib/module/NavigationBuilderContext.js +2 -1
  6. package/lib/module/NavigationBuilderContext.js.map +1 -1
  7. package/lib/module/NavigationIndependentTree.js +21 -11
  8. package/lib/module/NavigationIndependentTree.js.map +1 -1
  9. package/lib/module/NavigationProvider.js +10 -1
  10. package/lib/module/NavigationProvider.js.map +1 -1
  11. package/lib/module/NavigationRootContext.js +8 -0
  12. package/lib/module/NavigationRootContext.js.map +1 -0
  13. package/lib/module/NavigationStateContext.js +3 -0
  14. package/lib/module/NavigationStateContext.js.map +1 -1
  15. package/lib/module/SceneView.js +3 -1
  16. package/lib/module/SceneView.js.map +1 -1
  17. package/lib/module/StaticNavigation.js +54 -19
  18. package/lib/module/StaticNavigation.js.map +1 -1
  19. package/lib/module/createNavigationContainerRef.js +1 -1
  20. package/lib/module/createNavigationContainerRef.js.map +1 -1
  21. package/lib/module/findFocusedRoute.js.map +1 -1
  22. package/lib/module/getActionFromState.js.map +1 -1
  23. package/lib/module/getStateFromPath.js +15 -16
  24. package/lib/module/getStateFromPath.js.map +1 -1
  25. package/lib/module/getStateFromRouteParams.js +0 -1
  26. package/lib/module/getStateFromRouteParams.js.map +1 -1
  27. package/lib/module/index.js +1 -1
  28. package/lib/module/index.js.map +1 -1
  29. package/lib/module/internal.js +2 -0
  30. package/lib/module/internal.js.map +1 -0
  31. package/lib/module/types.js +0 -1
  32. package/lib/module/types.js.map +1 -1
  33. package/lib/module/useDescriptors.js +6 -4
  34. package/lib/module/useDescriptors.js.map +1 -1
  35. package/lib/module/useKeyedChildListeners.js +1 -0
  36. package/lib/module/useKeyedChildListeners.js.map +1 -1
  37. package/lib/module/useNavigation.js +2 -2
  38. package/lib/module/useNavigation.js.map +1 -1
  39. package/lib/module/useNavigationBuilder.js +14 -4
  40. package/lib/module/useNavigationBuilder.js.map +1 -1
  41. package/lib/module/useNavigationCache.js +23 -31
  42. package/lib/module/useNavigationCache.js.map +1 -1
  43. package/lib/module/useNavigationHelpers.js +18 -37
  44. package/lib/module/useNavigationHelpers.js.map +1 -1
  45. package/lib/module/useNavigationState.js +85 -33
  46. package/lib/module/useNavigationState.js.map +1 -1
  47. package/lib/module/useOnGetNavigation.js +26 -0
  48. package/lib/module/useOnGetNavigation.js.map +1 -0
  49. package/lib/module/useSyncState.js +25 -6
  50. package/lib/module/useSyncState.js.map +1 -1
  51. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  52. package/lib/typescript/src/DataLoading.d.ts.map +1 -1
  53. package/lib/typescript/src/NavigationBuilderContext.d.ts +4 -1
  54. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  55. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  56. package/lib/typescript/src/NavigationProvider.d.ts +9 -5
  57. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  58. package/lib/typescript/src/NavigationRootContext.d.ts +8 -0
  59. package/lib/typescript/src/NavigationRootContext.d.ts.map +1 -0
  60. package/lib/typescript/src/NavigationStateContext.d.ts +1 -0
  61. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  62. package/lib/typescript/src/SceneView.d.ts +2 -1
  63. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  64. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  65. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  66. package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
  67. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  68. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  69. package/lib/typescript/src/getStateFromPath.d.ts +1 -4
  70. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  71. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -1
  72. package/lib/typescript/src/index.d.ts +1 -1
  73. package/lib/typescript/src/index.d.ts.map +1 -1
  74. package/lib/typescript/src/internal.d.ts +3 -0
  75. package/lib/typescript/src/internal.d.ts.map +1 -0
  76. package/lib/typescript/src/types.d.ts +3 -6
  77. package/lib/typescript/src/types.d.ts.map +1 -1
  78. package/lib/typescript/src/useDescriptors.d.ts +2 -1
  79. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  80. package/lib/typescript/src/useKeyedChildListeners.d.ts +1 -0
  81. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  82. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  83. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  84. package/lib/typescript/src/useNavigationHelpers.d.ts +1 -2
  85. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  86. package/lib/typescript/src/useNavigationState.d.ts +7 -2
  87. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  88. package/lib/typescript/src/useOnGetNavigation.d.ts +12 -0
  89. package/lib/typescript/src/useOnGetNavigation.d.ts.map +1 -0
  90. package/lib/typescript/src/useSyncState.d.ts +1 -0
  91. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  92. package/package.json +11 -5
  93. package/src/BaseNavigationContainer.tsx +194 -97
  94. package/src/DataLoading.tsx +4 -1
  95. package/src/NavigationBuilderContext.tsx +10 -1
  96. package/src/NavigationIndependentTree.tsx +27 -16
  97. package/src/NavigationProvider.tsx +13 -6
  98. package/src/NavigationRootContext.tsx +11 -0
  99. package/src/NavigationStateContext.tsx +4 -0
  100. package/src/SceneView.tsx +4 -0
  101. package/src/StaticNavigation.tsx +77 -19
  102. package/src/createNavigationContainerRef.tsx +0 -2
  103. package/src/findFocusedRoute.tsx +7 -12
  104. package/src/getActionFromState.tsx +2 -1
  105. package/src/getStateFromPath.tsx +31 -43
  106. package/src/getStateFromRouteParams.tsx +0 -1
  107. package/src/index.tsx +1 -0
  108. package/src/internal.tsx +2 -0
  109. package/src/types.tsx +3 -7
  110. package/src/useDescriptors.tsx +6 -3
  111. package/src/useKeyedChildListeners.tsx +1 -0
  112. package/src/useNavigation.tsx +2 -2
  113. package/src/useNavigationBuilder.tsx +14 -4
  114. package/src/useNavigationCache.tsx +24 -33
  115. package/src/useNavigationHelpers.tsx +19 -38
  116. package/src/useNavigationState.tsx +104 -31
  117. package/src/useOnGetNavigation.tsx +29 -0
  118. package/src/useSyncState.tsx +27 -6
  119. package/lib/module/checkSerializable.js +0 -51
  120. package/lib/module/checkSerializable.js.map +0 -1
  121. package/lib/typescript/src/checkSerializable.d.ts +0 -14
  122. package/lib/typescript/src/checkSerializable.d.ts.map +0 -1
  123. package/src/checkSerializable.tsx +0 -90
@@ -3,24 +3,31 @@ import {
3
3
  type InitialState,
4
4
  type NavigationAction,
5
5
  type NavigationState,
6
+ type ParamListBase,
6
7
  type PartialState,
7
8
  type Route,
8
9
  } from '@react-navigation/routers';
9
10
  import * as React from 'react';
10
11
  import useLatestCallback from 'use-latest-callback';
12
+ import warnOnce from 'warn-once';
11
13
 
12
14
  import { checkDuplicateRouteNames } from './checkDuplicateRouteNames';
13
- import { checkSerializable } from './checkSerializable';
14
15
  import { ConsumedParamsContext } from './ConsumedParamsContext';
15
16
  import { NOT_INITIALIZED_ERROR } from './createNavigationContainerRef';
16
17
  import { EnsureSingleNavigator } from './EnsureSingleNavigator';
17
18
  import { findFocusedRoute } from './findFocusedRoute';
18
- import { NavigationBuilderContext } from './NavigationBuilderContext';
19
+ import {
20
+ NavigationBuilderContext,
21
+ type WithStackTrace,
22
+ } from './NavigationBuilderContext';
19
23
  import { NavigationContainerRefContext } from './NavigationContainerRefContext';
20
24
  import { NavigationIndependentTreeContext } from './NavigationIndependentTreeContext';
25
+ import { NavigationRootContext } from './NavigationRootContext';
21
26
  import { NavigationStateContext } from './NavigationStateContext';
22
27
  import { ThemeProvider } from './theming/ThemeProvider';
23
28
  import type {
29
+ EventListenerCallback,
30
+ GenericNavigation,
24
31
  NavigationContainerEventMap,
25
32
  NavigationContainerProps,
26
33
  NavigationContainerRef,
@@ -37,9 +44,6 @@ import { useSyncState } from './useSyncState';
37
44
 
38
45
  type State = NavigationState | PartialState<NavigationState> | undefined;
39
46
 
40
- const serializableWarnings: string[] = [];
41
- const duplicateNameWarnings: string[] = [];
42
-
43
47
  type Props<ParamList extends {}> = NavigationContainerProps & {
44
48
  ref?: React.Ref<NavigationContainerRef<ParamList>>;
45
49
  };
@@ -104,7 +108,7 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
104
108
  );
105
109
  }
106
110
 
107
- const { state, getState, setState, scheduleUpdate, flushUpdates } =
111
+ const { state, getState, setState, subscribe, scheduleUpdate, flushUpdates } =
108
112
  useSyncState<State>(() =>
109
113
  getPartialState(initialState == null ? undefined : initialState)
110
114
  );
@@ -125,6 +129,41 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
125
129
 
126
130
  const { keyedListeners, addKeyedListener } = useKeyedChildListeners();
127
131
 
132
+ const stackRef = React.useRef<string | undefined>(undefined);
133
+
134
+ const withStackTrace = React.useCallback<WithStackTrace>(
135
+ (entry, callback) => {
136
+ if (process.env.NODE_ENV === 'production' || stackRef.current != null) {
137
+ callback();
138
+ return;
139
+ }
140
+
141
+ const error = new Error();
142
+
143
+ if (Error.captureStackTrace) {
144
+ // Available on V8 and Hermes, omits the frames of `entry` and what it called
145
+ Error.captureStackTrace(error, entry);
146
+
147
+ stackRef.current = error.stack;
148
+ } else {
149
+ // Other engines always include them, so we drop the frames up to `entry` ourselves
150
+ const frames = error.stack?.split('\n') ?? [];
151
+ const index = frames.findIndex((frame) =>
152
+ frame.includes(`${entry.name}@`)
153
+ );
154
+
155
+ stackRef.current = frames.slice(index + 1).join('\n');
156
+ }
157
+
158
+ try {
159
+ callback();
160
+ } finally {
161
+ stackRef.current = undefined;
162
+ }
163
+ },
164
+ []
165
+ );
166
+
128
167
  const dispatch = useLatestCallback(
129
168
  (
130
169
  action: NavigationAction | ((state: NavigationState) => NavigationAction)
@@ -134,11 +173,13 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
134
173
  if (listener == null) {
135
174
  console.error(NOT_INITIALIZED_ERROR);
136
175
  } else {
137
- listener((navigation) =>
138
- React.startTransition(() => {
139
- navigation.dispatch(action);
140
- })
141
- );
176
+ withStackTrace(dispatch, () => {
177
+ listener((navigation) =>
178
+ React.startTransition(() => {
179
+ navigation.dispatch(action);
180
+ })
181
+ );
182
+ });
142
183
  }
143
184
  }
144
185
  );
@@ -161,7 +202,7 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
161
202
 
162
203
  const resetRoot = useLatestCallback(
163
204
  (state: PartialState<NavigationState> | NavigationState) => {
164
- const target = state?.key ?? keyedListeners.getState.root?.().key;
205
+ const target = keyedListeners.getState.root?.().key;
165
206
  const listener = listeners.focus[0];
166
207
 
167
208
  if (target == null || listener == null) {
@@ -199,29 +240,28 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
199
240
 
200
241
  const { addOptionsGetter, getCurrentOptions } = useOptionsGetters({});
201
242
 
202
- const navigation: NavigationContainerRef<ParamList> = React.useMemo(
243
+ const container: NavigationContainerRef<ParamList> = React.useMemo(
203
244
  () => ({
204
245
  ...Object.keys(CommonActions).reduce<any>((acc, name) => {
205
- acc[name] = (...args: any[]) =>
206
- // @ts-expect-error: this is ok
207
- dispatch(CommonActions[name](...args));
246
+ const helper = (...args: any[]) =>
247
+ withStackTrace(helper, () =>
248
+ // @ts-expect-error: this is ok
249
+ dispatch(CommonActions[name](...args))
250
+ );
251
+
252
+ acc[name] = helper;
208
253
 
209
254
  return acc;
210
255
  }, {}),
211
256
  ...emitter.create('root'),
212
257
  dispatch,
213
258
  resetRoot,
214
- isFocused: () => true,
215
259
  canGoBack,
216
- getParent: () => undefined,
217
260
  getState,
218
261
  getRootState,
219
262
  getCurrentRoute,
220
263
  getCurrentOptions,
221
264
  isReady,
222
- setOptions: () => {
223
- throw new Error('Cannot call setOptions outside a screen');
224
- },
225
265
  }),
226
266
  [
227
267
  canGoBack,
@@ -233,10 +273,112 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
233
273
  getState,
234
274
  isReady,
235
275
  resetRoot,
276
+ withStackTrace,
236
277
  ]
237
278
  );
238
279
 
239
- React.useImperativeHandle(ref, () => navigation, [navigation]);
280
+ const navigation: GenericNavigation<ParamListBase> = React.useMemo(() => {
281
+ const events = emitter.create('root');
282
+
283
+ const dispatch = (
284
+ thunk: NavigationAction | ((state: NavigationState) => NavigationAction)
285
+ ) => {
286
+ const root = keyedListeners.getNavigation.root?.();
287
+
288
+ if (root == null) {
289
+ console.error(NOT_INITIALIZED_ERROR);
290
+ return;
291
+ }
292
+
293
+ withStackTrace(dispatch, () => {
294
+ React.startTransition(() => {
295
+ root.dispatch(thunk);
296
+ });
297
+ });
298
+ };
299
+
300
+ const helpers = Object.keys(CommonActions).reduce<any>((acc, name) => {
301
+ const helper = (...args: any) => {
302
+ if (
303
+ name === 'setParams' ||
304
+ name === 'replaceParams' ||
305
+ name === 'pushParams'
306
+ ) {
307
+ throw new Error(`Cannot call ${name} outside a screen`);
308
+ }
309
+
310
+ withStackTrace(helper, () =>
311
+ // @ts-expect-error name is a valid key, but TypeScript cannot infer it.
312
+ dispatch(CommonActions[name](...args))
313
+ );
314
+ };
315
+
316
+ acc[name] = helper;
317
+
318
+ return acc;
319
+ }, {});
320
+
321
+ const listeners = new WeakMap<
322
+ (...args: never[]) => void,
323
+ EventListenerCallback<NavigationContainerEventMap, 'state'>
324
+ >();
325
+
326
+ return {
327
+ ...helpers,
328
+ dispatch,
329
+ addListener: (type, callback) => {
330
+ if (type === 'state') {
331
+ let listener = listeners.get(callback);
332
+
333
+ if (listener === undefined) {
334
+ // Root's state change events can contain stale and undefined state
335
+ // But navigation objects should only receive non-stale state
336
+ // So we add a wrapper to filter out stale events
337
+ listener = (event) => {
338
+ if (event.data.state?.stale === false) {
339
+ // @ts-expect-error TypeScript doesn't narrow the generic event callback with its type.
340
+ callback(event);
341
+ }
342
+ };
343
+
344
+ listeners.set(callback, listener);
345
+ }
346
+
347
+ return events.addListener('state', listener);
348
+ }
349
+
350
+ return () => {};
351
+ },
352
+ removeListener: (type, callback) => {
353
+ if (type === 'state') {
354
+ const listener = listeners.get(callback);
355
+
356
+ if (listener) {
357
+ events.removeListener('state', listener);
358
+ listeners.delete(callback);
359
+ }
360
+ }
361
+ },
362
+ canGoBack: () =>
363
+ keyedListeners.getNavigation.root?.().canGoBack() ?? false,
364
+ getState: () => keyedListeners.getState.root?.(),
365
+ getParent: (routeName?: string) => {
366
+ if (routeName !== undefined) {
367
+ throw new Error(
368
+ `Couldn't find a navigation object for '${routeName}' because it's called outside a screen. Is your component inside a screen?`
369
+ );
370
+ }
371
+
372
+ return undefined;
373
+ },
374
+ setOptions: () => {
375
+ throw new Error('Cannot call setOptions outside a screen');
376
+ },
377
+ isFocused: () => true,
378
+ };
379
+ }, [emitter, keyedListeners, withStackTrace]);
380
+
381
+ React.useImperativeHandle(ref, () => container, [container]);
240
382
 
241
383
  const onDispatchAction = useLatestCallback(
242
384
  (action: NavigationAction, noop: boolean) => {
@@ -276,8 +418,6 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
276
418
  });
277
419
  });
278
420
 
279
- const stackRef = React.useRef<string | undefined>(undefined);
280
-
281
421
  const lastEmittedStateRef = React.useRef<State>(undefined);
282
422
 
283
423
  const getIsStateEmitted = useLatestCallback(
@@ -294,7 +434,7 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
294
434
  getIsStateEmitted,
295
435
  scheduleUpdate,
296
436
  flushUpdates,
297
- stackRef,
437
+ withStackTrace,
298
438
  }),
299
439
  [
300
440
  addListener,
@@ -305,6 +445,7 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
305
445
  getIsStateEmitted,
306
446
  scheduleUpdate,
307
447
  flushUpdates,
448
+ withStackTrace,
308
449
  ]
309
450
  );
310
451
 
@@ -317,12 +458,22 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
317
458
  state,
318
459
  getState,
319
460
  setState,
461
+ subscribe,
320
462
  getKey,
321
463
  setKey,
322
464
  getIsInitial,
323
465
  addOptionsGetter,
324
466
  }),
325
- [state, getState, setState, getKey, setKey, getIsInitial, addOptionsGetter]
467
+ [
468
+ state,
469
+ getState,
470
+ setState,
471
+ subscribe,
472
+ getKey,
473
+ setKey,
474
+ getIsInitial,
475
+ addOptionsGetter,
476
+ ]
326
477
  );
327
478
 
328
479
  const onReadyRef = React.useRef(onReady);
@@ -349,59 +500,6 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
349
500
 
350
501
  if (process.env.NODE_ENV !== 'production') {
351
502
  if (hydratedState !== undefined) {
352
- const serializableResult = checkSerializable(hydratedState);
353
-
354
- if (!serializableResult.serializable) {
355
- const { location, reason } = serializableResult;
356
-
357
- let path = '';
358
- let pointer: Record<any, any> = hydratedState;
359
- let params = false;
360
-
361
- for (let i = 0; i < location.length; i++) {
362
- const curr = location[i];
363
- const prev = location[i - 1];
364
-
365
- if (curr == null) {
366
- continue;
367
- }
368
-
369
- pointer = pointer[curr];
370
-
371
- if (!params && curr === 'state') {
372
- continue;
373
- } else if (!params && curr === 'routes') {
374
- if (path) {
375
- path += ' > ';
376
- }
377
- } else if (
378
- !params &&
379
- typeof curr === 'number' &&
380
- prev === 'routes'
381
- ) {
382
- path += pointer?.name;
383
- } else if (!params) {
384
- path += ` > ${curr}`;
385
- params = true;
386
- } else {
387
- if (typeof curr === 'number' || /^[0-9]+$/.test(curr)) {
388
- path += `[${curr}]`;
389
- } else if (/^[a-z$_]+$/i.test(curr)) {
390
- path += `.${curr}`;
391
- } else {
392
- path += `[${JSON.stringify(curr)}]`;
393
- }
394
- }
395
- }
396
-
397
- const message = `Non-serializable values were found in the navigation state. Check:\n\n${path} (${reason})\n\nThis can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details.`;
398
-
399
- if (!serializableWarnings.includes(message)) {
400
- serializableWarnings.push(message);
401
- console.warn(message);
402
- }
403
- }
404
-
405
503
  const duplicateRouteNamesResult =
406
504
  checkDuplicateRouteNames(hydratedState);
407
505
 
@@ -410,10 +508,7 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
410
508
  (locations) => `\n${locations.join(', ')}`
411
509
  )}\n\nThis can cause confusing behavior during navigation. Consider using unique names for each screen instead.`;
412
510
 
413
- if (!duplicateNameWarnings.includes(message)) {
414
- duplicateNameWarnings.push(message);
415
- console.warn(message);
416
- }
511
+ warnOnce(true, message);
417
512
  }
418
513
  }
419
514
  }
@@ -475,20 +570,22 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
475
570
 
476
571
  return (
477
572
  <NavigationIndependentTreeContext.Provider value={false}>
478
- <NavigationContainerRefContext.Provider value={navigation}>
479
- <NavigationBuilderContext.Provider value={builderContext}>
480
- <NavigationStateContext.Provider value={context}>
481
- <ConsumedParamsContext.Provider value={consumedParams}>
482
- <UnhandledActionContext.Provider
483
- value={onUnhandledAction ?? defaultOnUnhandledAction}
484
- >
485
- <EnsureSingleNavigator>
486
- <ThemeProvider value={theme}>{children}</ThemeProvider>
487
- </EnsureSingleNavigator>
488
- </UnhandledActionContext.Provider>
489
- </ConsumedParamsContext.Provider>
490
- </NavigationStateContext.Provider>
491
- </NavigationBuilderContext.Provider>
573
+ <NavigationContainerRefContext.Provider value={container}>
574
+ <NavigationRootContext.Provider value={navigation}>
575
+ <NavigationBuilderContext.Provider value={builderContext}>
576
+ <NavigationStateContext.Provider value={context}>
577
+ <ConsumedParamsContext.Provider value={consumedParams}>
578
+ <UnhandledActionContext.Provider
579
+ value={onUnhandledAction ?? defaultOnUnhandledAction}
580
+ >
581
+ <EnsureSingleNavigator>
582
+ <ThemeProvider value={theme}>{children}</ThemeProvider>
583
+ </EnsureSingleNavigator>
584
+ </UnhandledActionContext.Provider>
585
+ </ConsumedParamsContext.Provider>
586
+ </NavigationStateContext.Provider>
587
+ </NavigationBuilderContext.Provider>
588
+ </NavigationRootContext.Provider>
492
589
  </NavigationContainerRefContext.Provider>
493
590
  </NavigationIndependentTreeContext.Provider>
494
591
  );
@@ -134,7 +134,10 @@ export function getLoaderForStateChange(
134
134
  const nested =
135
135
  'config' in item
136
136
  ? item
137
- : 'screen' in item && 'config' in item.screen
137
+ : 'screen' in item &&
138
+ // Nested navigators cannot be defined as a getter
139
+ Object.getOwnPropertyDescriptor(item, 'screen')?.get == null &&
140
+ 'config' in item.screen
138
141
  ? item.screen
139
142
  : undefined;
140
143
 
@@ -15,6 +15,7 @@ export type ListenerMap = {
15
15
 
16
16
  export type KeyedListenerMap = {
17
17
  getState: GetStateListener;
18
+ getNavigation: GetNavigationListener;
18
19
  beforeRemove: ChildBeforeRemoveListener;
19
20
  };
20
21
 
@@ -47,6 +48,13 @@ export type FocusedNavigationListener = <T>(
47
48
 
48
49
  export type GetStateListener = () => NavigationState;
49
50
 
51
+ export type GetNavigationListener = () => NavigationHelpers<ParamListBase>;
52
+
53
+ export type WithStackTrace = (
54
+ entry: (...args: never[]) => void,
55
+ callback: () => void
56
+ ) => void;
57
+
50
58
  export type ChildBeforeRemoveListener = (
51
59
  action: NavigationAction,
52
60
  nextState: NavigationState | PartialState<NavigationState> | undefined
@@ -73,7 +81,7 @@ export const NavigationBuilderContext = React.createContext<{
73
81
  getIsStateEmitted: () => boolean;
74
82
  scheduleUpdate: (callback: () => void) => void;
75
83
  flushUpdates: () => void;
76
- stackRef?: React.RefObject<string | undefined> | undefined;
84
+ withStackTrace: WithStackTrace;
77
85
  }>({
78
86
  onDispatchAction: () => undefined,
79
87
  onEmitEvent: () => undefined,
@@ -85,4 +93,5 @@ export const NavigationBuilderContext = React.createContext<{
85
93
  flushUpdates: () => {
86
94
  throw new Error("Couldn't find a context for flushing updates.");
87
95
  },
96
+ withStackTrace: (_entry, callback) => callback(),
88
97
  });
@@ -3,12 +3,17 @@ import * as React from 'react';
3
3
  import { NavigationFocusedRouteStateContext } from './NavigationFocusedRouteStateContext';
4
4
  import { NavigationIndependentTreeContext } from './NavigationIndependentTreeContext';
5
5
  import {
6
+ IsScreenContext,
6
7
  NamedRouteContextListContext,
7
8
  NavigationContext,
8
9
  NavigationRouteContext,
9
10
  } from './NavigationProvider';
11
+ import { NavigationRootContext } from './NavigationRootContext';
10
12
  import { IsFocusedContext } from './useIsFocused';
11
- import { NamedNavigationStateListenerListContext } from './useNavigationState';
13
+ import {
14
+ NamedNavigationStateListenerListContext,
15
+ NavigationStateListenerContext,
16
+ } from './useNavigationState';
12
17
 
13
18
  /**
14
19
  * Component to make the child navigation container independent of parent containers.
@@ -20,20 +25,26 @@ export function NavigationIndependentTree({
20
25
  }) {
21
26
  return (
22
27
  // We need to clear any existing contexts for nested independent container to work correctly
23
- <NamedNavigationStateListenerListContext.Provider value={undefined}>
24
- <NamedRouteContextListContext.Provider value={undefined}>
25
- <NavigationRouteContext.Provider value={undefined}>
26
- <NavigationContext.Provider value={undefined}>
27
- <NavigationFocusedRouteStateContext.Provider value={undefined}>
28
- <IsFocusedContext.Provider value={undefined}>
29
- <NavigationIndependentTreeContext.Provider value={true}>
30
- {children}
31
- </NavigationIndependentTreeContext.Provider>
32
- </IsFocusedContext.Provider>
33
- </NavigationFocusedRouteStateContext.Provider>
34
- </NavigationContext.Provider>
35
- </NavigationRouteContext.Provider>
36
- </NamedRouteContextListContext.Provider>
37
- </NamedNavigationStateListenerListContext.Provider>
28
+ <NavigationStateListenerContext.Provider value={undefined}>
29
+ <NamedNavigationStateListenerListContext.Provider value={undefined}>
30
+ <NamedRouteContextListContext.Provider value={undefined}>
31
+ <NavigationRouteContext.Provider value={undefined}>
32
+ <NavigationContext.Provider value={undefined}>
33
+ <NavigationRootContext.Provider value={undefined}>
34
+ <NavigationFocusedRouteStateContext.Provider value={undefined}>
35
+ <IsFocusedContext.Provider value={undefined}>
36
+ <NavigationIndependentTreeContext.Provider value={true}>
37
+ <IsScreenContext.Provider value={false}>
38
+ {children}
39
+ </IsScreenContext.Provider>
40
+ </NavigationIndependentTreeContext.Provider>
41
+ </IsFocusedContext.Provider>
42
+ </NavigationFocusedRouteStateContext.Provider>
43
+ </NavigationRootContext.Provider>
44
+ </NavigationContext.Provider>
45
+ </NavigationRouteContext.Provider>
46
+ </NamedRouteContextListContext.Provider>
47
+ </NamedNavigationStateListenerListContext.Provider>
48
+ </NavigationStateListenerContext.Provider>
38
49
  );
39
50
  }
@@ -20,11 +20,10 @@ export const NavigationContext = React.createContext<
20
20
  NavigationProp<ParamListBase> | undefined
21
21
  >(undefined);
22
22
 
23
- type Props = {
24
- route: Route<string>;
25
- navigation: NavigationProp<ParamListBase>;
26
- children: React.ReactNode;
27
- };
23
+ /**
24
+ * Context which indicates whether the component is inside a screen.
25
+ */
26
+ export const IsScreenContext = React.createContext(false);
28
27
 
29
28
  /**
30
29
  * Component to provide the navigation and route contexts to its children.
@@ -33,6 +32,12 @@ export const NamedRouteContextListContext = React.createContext<
33
32
  Record<string, React.Context<Route<string>>> | undefined
34
33
  >(undefined);
35
34
 
35
+ type Props = {
36
+ route: Route<string>;
37
+ navigation: NavigationProp<ParamListBase>;
38
+ children: React.ReactNode;
39
+ };
40
+
36
41
  export function NavigationProvider({ route, navigation, children }: Props) {
37
42
  const NamedRouteContext = useLazyValue(() => React.createContext(route));
38
43
 
@@ -64,7 +69,9 @@ export function NavigationProvider({ route, navigation, children }: Props) {
64
69
  <NavigationRouteContext.Provider value={route}>
65
70
  <NavigationContext.Provider value={navigation}>
66
71
  <IsFocusedContext.Provider value={isFocused}>
67
- {children}
72
+ <IsScreenContext.Provider value={true}>
73
+ {children}
74
+ </IsScreenContext.Provider>
68
75
  </IsFocusedContext.Provider>
69
76
  </NavigationContext.Provider>
70
77
  </NavigationRouteContext.Provider>
@@ -0,0 +1,11 @@
1
+ import type { ParamListBase } from '@react-navigation/routers';
2
+ import * as React from 'react';
3
+
4
+ import type { GenericNavigation } from './types';
5
+
6
+ /**
7
+ * Context which holds the navigation object for components outside a screen.
8
+ */
9
+ export const NavigationRootContext = React.createContext<
10
+ GenericNavigation<ParamListBase> | undefined
11
+ >(undefined);
@@ -13,6 +13,7 @@ export const NavigationStateContext = React.createContext<{
13
13
  setState: (
14
14
  state: NavigationState | PartialState<NavigationState> | undefined
15
15
  ) => void;
16
+ subscribe: (callback: () => void) => () => void;
16
17
  getIsInitial: () => boolean;
17
18
  addOptionsGetter?:
18
19
  | ((key: string, getter: () => object | undefined | null) => void)
@@ -32,6 +33,9 @@ export const NavigationStateContext = React.createContext<{
32
33
  get setState(): any {
33
34
  throw new Error(MISSING_CONTEXT_ERROR);
34
35
  },
36
+ get subscribe(): any {
37
+ throw new Error(MISSING_CONTEXT_ERROR);
38
+ },
35
39
  get getIsInitial(): any {
36
40
  throw new Error(MISSING_CONTEXT_ERROR);
37
41
  },
package/src/SceneView.tsx CHANGED
@@ -24,6 +24,7 @@ type Props<State extends NavigationState, ScreenOptions extends {}> = {
24
24
  routeState: NavigationState | PartialState<NavigationState> | undefined;
25
25
  getState: () => State;
26
26
  setState: (state: State) => void;
27
+ subscribe: (callback: () => void) => () => void;
27
28
  options: object;
28
29
  clearOptions: () => void;
29
30
  };
@@ -42,6 +43,7 @@ export function SceneView<
42
43
  routeState,
43
44
  getState,
44
45
  setState,
46
+ subscribe,
45
47
  options,
46
48
  clearOptions,
47
49
  }: Props<State, ScreenOptions>) {
@@ -152,6 +154,7 @@ export function SceneView<
152
154
  state: routeState,
153
155
  getState: getCurrentState,
154
156
  setState: setCurrentState,
157
+ subscribe,
155
158
  getKey,
156
159
  setKey,
157
160
  getIsInitial,
@@ -161,6 +164,7 @@ export function SceneView<
161
164
  routeState,
162
165
  getCurrentState,
163
166
  setCurrentState,
167
+ subscribe,
164
168
  getKey,
165
169
  setKey,
166
170
  getIsInitial,