@react-navigation/core 7.17.2 → 7.17.4

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 (40) hide show
  1. package/lib/module/StaticNavigation.js +0 -19
  2. package/lib/module/StaticNavigation.js.map +1 -1
  3. package/lib/module/checkSerializable.js +11 -4
  4. package/lib/module/checkSerializable.js.map +1 -1
  5. package/lib/module/getActionFromState.js +15 -2
  6. package/lib/module/getActionFromState.js.map +1 -1
  7. package/lib/module/getStateFromPath.js +98 -74
  8. package/lib/module/getStateFromPath.js.map +1 -1
  9. package/lib/module/index.js.map +1 -1
  10. package/lib/module/types.js +55 -0
  11. package/lib/module/types.js.map +1 -1
  12. package/lib/module/useEventEmitter.js +28 -29
  13. package/lib/module/useEventEmitter.js.map +1 -1
  14. package/lib/module/useNavigationBuilder.js +48 -47
  15. package/lib/module/useNavigationBuilder.js.map +1 -1
  16. package/lib/module/useRouteCache.js +2 -1
  17. package/lib/module/useRouteCache.js.map +1 -1
  18. package/lib/typescript/src/StaticNavigation.d.ts +2 -63
  19. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  20. package/lib/typescript/src/checkSerializable.d.ts +5 -3
  21. package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
  22. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  23. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  24. package/lib/typescript/src/index.d.ts +1 -1
  25. package/lib/typescript/src/index.d.ts.map +1 -1
  26. package/lib/typescript/src/types.d.ts +102 -2
  27. package/lib/typescript/src/types.d.ts.map +1 -1
  28. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  29. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  30. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  31. package/package.json +3 -3
  32. package/src/StaticNavigation.tsx +1 -91
  33. package/src/checkSerializable.tsx +22 -12
  34. package/src/getActionFromState.tsx +20 -4
  35. package/src/getStateFromPath.tsx +147 -100
  36. package/src/index.tsx +0 -1
  37. package/src/types.tsx +138 -4
  38. package/src/useEventEmitter.tsx +33 -38
  39. package/src/useNavigationBuilder.tsx +77 -76
  40. package/src/useRouteCache.tsx +2 -1
@@ -63,6 +63,14 @@ type NavigatorRoute = {
63
63
  params?: NavigatorScreenParams<ParamListBase>;
64
64
  };
65
65
 
66
+ const isNavigationState = (
67
+ state: unknown
68
+ ): state is NavigationState | PartialState<NavigationState> =>
69
+ state != null &&
70
+ typeof state === 'object' &&
71
+ 'routes' in state &&
72
+ Array.isArray(state.routes);
73
+
66
74
  const isScreen = (
67
75
  child: React.ReactElement<unknown>
68
76
  ): child is React.ReactElement<{
@@ -279,8 +287,10 @@ const getRouteConfigsFromChildren = <
279
287
  };
280
288
 
281
289
  const getStateFromParams = (params: NavigatorRoute['params']) => {
282
- if (params?.state != null) {
283
- return params.state;
290
+ const state = params?.state;
291
+
292
+ if (isNavigationState(state)) {
293
+ return state;
284
294
  } else if (typeof params?.screen === 'string' && params?.initial !== false) {
285
295
  return {
286
296
  routes: [
@@ -376,44 +386,7 @@ export function useNavigationBuilder<
376
386
  return original;
377
387
  });
378
388
 
379
- const screens = routeConfigs.reduce<
380
- Record<string, ScreenConfigWithParent<State, ScreenOptions, EventMap>>
381
- >((acc, config) => {
382
- if (config.props.name in acc) {
383
- throw new Error(
384
- `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`
385
- );
386
- }
387
-
388
- acc[config.props.name] = config;
389
- return acc;
390
- }, {});
391
-
392
389
  const routeNames = routeConfigs.map((config) => config.props.name);
393
- const routeKeyList = routeNames.reduce<Record<string, React.Key | undefined>>(
394
- (acc, curr) => {
395
- acc[curr] = screens[curr].keys.map((key) => key ?? '').join(':');
396
- return acc;
397
- },
398
- {}
399
- );
400
- const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
401
- (acc, curr) => {
402
- const { initialParams } = screens[curr].props;
403
- acc[curr] = initialParams;
404
- return acc;
405
- },
406
- {}
407
- );
408
- const routeGetIdList = routeNames.reduce<
409
- RouterConfigOptions['routeGetIdList']
410
- >(
411
- (acc, curr) =>
412
- Object.assign(acc, {
413
- [curr]: screens[curr].props.getId,
414
- }),
415
- {}
416
- );
417
390
 
418
391
  if (!routeNames.length) {
419
392
  throw new Error(
@@ -421,6 +394,31 @@ export function useNavigationBuilder<
421
394
  );
422
395
  }
423
396
 
397
+ const screens: Record<
398
+ string,
399
+ ScreenConfigWithParent<State, ScreenOptions, EventMap>
400
+ > = {};
401
+
402
+ const routeKeyList: Record<string, React.Key | undefined> = {};
403
+ const routeParamList: Record<string, object | undefined> = {};
404
+ const routeGetIdList: RouterConfigOptions['routeGetIdList'] = {};
405
+
406
+ for (const config of routeConfigs) {
407
+ const name = config.props.name;
408
+
409
+ if (name in screens) {
410
+ throw new Error(
411
+ `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${name}')`
412
+ );
413
+ }
414
+
415
+ screens[name] = config;
416
+ routeKeyList[name] = config.keys.map((key) => key ?? '').join(':');
417
+ routeParamList[name] = config.props.initialParams;
418
+
419
+ Object.assign(routeGetIdList, { [name]: config.props.getId });
420
+ }
421
+
424
422
  const isStateValid = React.useCallback(
425
423
  (state: NavigationState | PartialState<NavigationState>) =>
426
424
  state.type === undefined || state.type === router.type,
@@ -659,24 +657,21 @@ export function useNavigationBuilder<
659
657
 
660
658
  if (route?.params && !didConsumeNestedParams) {
661
659
  let action: CommonActions.Action | undefined;
660
+ const stateFromParams = route.params.state;
662
661
 
663
- if (
664
- typeof route.params.state === 'object' &&
665
- route.params.state != null &&
666
- !isNestedParamsConsumed
667
- ) {
662
+ if (isNavigationState(stateFromParams) && !isNestedParamsConsumed) {
668
663
  didConsumeNestedParams = true;
669
664
 
670
665
  if (
671
666
  options.UNSTABLE_routeNamesChangeBehavior === 'lastUnhandled' &&
672
- doesStateHaveOnlyInvalidRoutes(route.params.state)
667
+ doesStateHaveOnlyInvalidRoutes(stateFromParams)
673
668
  ) {
674
- if (route.params.state !== unhandledState) {
675
- setUnhandledState(route.params.state);
669
+ if (stateFromParams !== unhandledState) {
670
+ setUnhandledState(stateFromParams);
676
671
  }
677
672
  } else {
678
673
  // If the route was updated with new state, we should reset to it
679
- action = CommonActions.reset(route.params.state);
674
+ action = CommonActions.reset(stateFromParams);
680
675
  }
681
676
  } else if (
682
677
  typeof route.params.screen === 'string' &&
@@ -835,35 +830,41 @@ export function useNavigationBuilder<
835
830
  return;
836
831
  }
837
832
 
838
- const navigation = descriptors[route.key].navigation;
839
-
840
- const listeners = ([] as (((e: any) => void) | undefined)[])
841
- .concat(
842
- // Get an array of listeners for all screens + common listeners on navigator
843
- ...[
844
- screenListeners,
845
- ...routeNames.map((name) => {
846
- const { listeners } = screens[name].props;
847
- return listeners;
848
- }),
849
- ].map((listeners) => {
850
- const map =
851
- typeof listeners === 'function'
852
- ? listeners({ route: route as any, navigation })
853
- : listeners;
854
-
855
- return map
856
- ? Object.keys(map)
857
- .filter((type) => type === e.type)
858
- .map((type) => map?.[type])
859
- : undefined;
860
- })
861
- )
862
- // We don't want same listener to be called multiple times for same event
863
- // So we remove any duplicate functions from the array
864
- .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
833
+ const hasPerScreenListeners = routeNames.some(
834
+ (name) => screens[name].props.listeners != null
835
+ );
836
+
837
+ if (screenListeners != null || hasPerScreenListeners) {
838
+ const navigation = descriptors[route.key].navigation;
839
+
840
+ const listeners = ([] as (((e: any) => void) | undefined)[])
841
+ .concat(
842
+ // Get an array of listeners for all screens + common listeners on navigator
843
+ ...[
844
+ screenListeners,
845
+ ...routeNames.map((name) => {
846
+ const { listeners } = screens[name].props;
847
+ return listeners;
848
+ }),
849
+ ].map((listeners) => {
850
+ const map =
851
+ typeof listeners === 'function'
852
+ ? listeners({ route: route as any, navigation })
853
+ : listeners;
854
+
855
+ return map
856
+ ? Object.keys(map)
857
+ .filter((type) => type === e.type)
858
+ .map((type) => map?.[type])
859
+ : undefined;
860
+ })
861
+ )
862
+ // We don't want same listener to be called multiple times for same event
863
+ // So we remove any duplicate functions from the array
864
+ .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
865
865
 
866
- listeners.forEach((listener) => listener?.(e));
866
+ listeners.forEach((listener) => listener?.(e));
867
+ }
867
868
  });
868
869
 
869
870
  useFocusEvents({ state, emitter });
@@ -36,9 +36,10 @@ export function useRouteCache<State extends NavigationState>(
36
36
  proxy = routeWithoutState;
37
37
  }
38
38
 
39
- if (process.env.NODE_ENV !== 'production') {
39
+ if (process.env.NODE_ENV !== 'production' && proxy !== previous) {
40
40
  // FIXME: since the state is updated with mutation, the route object cannot be frozen
41
41
  // As a workaround, loop through the object and make the properties readonly
42
+ // Only needed once per proxy - skip if we're reusing a previously-frozen one
42
43
  for (const key in proxy) {
43
44
  // @ts-expect-error: this is fine since we are looping through the object
44
45
  const value = proxy[key];