@react-navigation/routers 7.0.0-alpha.4 → 7.0.0-alpha.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.
@@ -4,6 +4,7 @@ import { BaseRouter } from './BaseRouter';
4
4
  import type {
5
5
  CommonNavigationAction,
6
6
  DefaultRouterOptions,
7
+ NavigationRoute,
7
8
  NavigationState,
8
9
  ParamListBase,
9
10
  Route,
@@ -43,6 +44,20 @@ export type StackActionType =
43
44
  };
44
45
  source?: string;
45
46
  target?: string;
47
+ }
48
+ | {
49
+ type: 'REMOVE';
50
+ payload: {
51
+ name: string;
52
+ params?: object;
53
+ };
54
+ source?: string;
55
+ target?: string;
56
+ }
57
+ | {
58
+ type: 'RETAIN';
59
+ source?: string;
60
+ target?: string;
46
61
  };
47
62
 
48
63
  export type StackRouterOptions = DefaultRouterOptions;
@@ -53,6 +68,10 @@ export type StackNavigationState<ParamList extends ParamListBase> =
53
68
  * Type of the router, in this case, it's stack.
54
69
  */
55
70
  type: 'stack';
71
+ /**
72
+ * List of routes, which are supposed to be preloaded before navigating to.
73
+ */
74
+ preloadedRoutes: NavigationRoute<ParamList, keyof ParamList>[];
56
75
  };
57
76
 
58
77
  export type StackActionHelpers<ParamList extends ParamListBase> = {
@@ -113,6 +132,29 @@ export type StackActionHelpers<ParamList extends ParamListBase> = {
113
132
  | [screen: RouteName, params: ParamList[RouteName], merge: boolean]
114
133
  : never
115
134
  ): void;
135
+
136
+ /**
137
+ * Remove a screen from the preloaded list in the navigator.
138
+ *
139
+ * @param name Name of the route to remove preload.
140
+ * @param [params] Params object for the route.
141
+ */
142
+ remove<RouteName extends keyof ParamList>(
143
+ ...args: RouteName extends unknown
144
+ ? undefined extends ParamList[RouteName]
145
+ ?
146
+ | [screen: RouteName]
147
+ | [screen: RouteName, params: ParamList[RouteName]]
148
+ : [screen: RouteName, params: ParamList[RouteName]]
149
+ : never
150
+ ): void;
151
+
152
+ /**
153
+ * Removes a screen from the active routes, at the same time
154
+ * retaining the screen in the preloaded screens list,
155
+ * so it is not getting detached.
156
+ */
157
+ retain(): void;
116
158
  };
117
159
 
118
160
  export const StackActions = {
@@ -131,6 +173,12 @@ export const StackActions = {
131
173
  popTo(name: string, params?: object, merge?: boolean): StackActionType {
132
174
  return { type: 'POP_TO', payload: { name, params, merge } };
133
175
  },
176
+ remove(name: string, params?: object): StackActionType {
177
+ return { type: 'REMOVE', payload: { name, params } };
178
+ },
179
+ retain(): StackActionType {
180
+ return { type: 'RETAIN' };
181
+ },
134
182
  };
135
183
 
136
184
  export function StackRouter(options: StackRouterOptions) {
@@ -155,6 +203,7 @@ export function StackRouter(options: StackRouterOptions) {
155
203
  key: `stack-${nanoid()}`,
156
204
  index: 0,
157
205
  routeNames,
206
+ preloadedRoutes: [],
158
207
  routes: [
159
208
  {
160
209
  key: `${initialRouteName}-${nanoid()}`,
@@ -174,20 +223,35 @@ export function StackRouter(options: StackRouterOptions) {
174
223
 
175
224
  const routes = state.routes
176
225
  .filter((route) => routeNames.includes(route.name))
177
- .map(
178
- (route) =>
179
- ({
180
- ...route,
181
- key: route.key || `${route.name}-${nanoid()}`,
182
- params:
183
- routeParamList[route.name] !== undefined
184
- ? {
185
- ...routeParamList[route.name],
186
- ...route.params,
187
- }
188
- : route.params,
189
- }) as Route<string>
190
- );
226
+ .map((route) => ({
227
+ ...route,
228
+ key: route.key || `${route.name}-${nanoid()}`,
229
+ params:
230
+ routeParamList[route.name] !== undefined
231
+ ? {
232
+ ...routeParamList[route.name],
233
+ ...route.params,
234
+ }
235
+ : route.params,
236
+ }));
237
+
238
+ const preloadedRoutes =
239
+ state.preloadedRoutes
240
+ ?.filter((route) => routeNames.includes(route.name))
241
+ .map(
242
+ (route) =>
243
+ ({
244
+ ...route,
245
+ key: route.key || `${route.name}-${nanoid()}`,
246
+ params:
247
+ routeParamList[route.name] !== undefined
248
+ ? {
249
+ ...routeParamList[route.name],
250
+ ...route.params,
251
+ }
252
+ : route.params,
253
+ }) as Route<string>
254
+ ) ?? [];
191
255
 
192
256
  if (routes.length === 0) {
193
257
  const initialRouteName =
@@ -209,6 +273,7 @@ export function StackRouter(options: StackRouterOptions) {
209
273
  index: routes.length - 1,
210
274
  routeNames,
211
275
  routes,
276
+ preloadedRoutes,
212
277
  };
213
278
  },
214
279
 
@@ -327,6 +392,14 @@ export function StackRouter(options: StackRouterOptions) {
327
392
  }
328
393
  }
329
394
 
395
+ if (!route) {
396
+ route = state.preloadedRoutes.find(
397
+ (route) =>
398
+ route.name === action.payload.name &&
399
+ id === getId?.({ params: route.params })
400
+ );
401
+ }
402
+
330
403
  let params;
331
404
 
332
405
  if (action.type === 'NAVIGATE' && action.payload.merge && route) {
@@ -379,11 +452,23 @@ export function StackRouter(options: StackRouterOptions) {
379
452
  return {
380
453
  ...state,
381
454
  index: routes.length - 1,
455
+ preloadedRoutes: state.preloadedRoutes.filter(
456
+ (route) => routes[routes.length - 1].key !== route.key
457
+ ),
382
458
  routes,
383
459
  };
384
460
  }
385
461
 
386
462
  case 'NAVIGATE_DEPRECATED': {
463
+ if (
464
+ state.preloadedRoutes.find(
465
+ (route) =>
466
+ route.name === action.payload.name &&
467
+ id === getId?.({ params: route.params })
468
+ )
469
+ ) {
470
+ return null;
471
+ }
387
472
  if (!state.routeNames.includes(action.payload.name)) {
388
473
  return null;
389
474
  }
@@ -606,6 +691,96 @@ export function StackRouter(options: StackRouterOptions) {
606
691
 
607
692
  return null;
608
693
 
694
+ case 'PRELOAD': {
695
+ const getId = options.routeGetIdList[action.payload.name];
696
+ const id = getId?.({ params: action.payload.params });
697
+
698
+ let route: Route<string> | undefined;
699
+
700
+ if (id !== undefined) {
701
+ route = state.routes.find(
702
+ (route) =>
703
+ route.name === action.payload.name &&
704
+ id === getId?.({ params: route.params })
705
+ );
706
+ }
707
+
708
+ if (route) {
709
+ return {
710
+ ...state,
711
+ routes: state.routes.map((r) => {
712
+ if (r.key !== route?.key) {
713
+ return r;
714
+ }
715
+ return {
716
+ ...r,
717
+ params:
718
+ routeParamList[action.payload.name] !== undefined
719
+ ? {
720
+ ...routeParamList[action.payload.name],
721
+ ...action.payload.params,
722
+ }
723
+ : action.payload.params,
724
+ };
725
+ }),
726
+ };
727
+ } else {
728
+ return {
729
+ ...state,
730
+ preloadedRoutes: state.preloadedRoutes
731
+ .filter(
732
+ (r) =>
733
+ r.name !== action.payload.name ||
734
+ id !== getId?.({ params: r.params })
735
+ )
736
+ .concat({
737
+ key: `${action.payload.name}-${nanoid()}`,
738
+ name: action.payload.name,
739
+ params:
740
+ routeParamList[action.payload.name] !== undefined
741
+ ? {
742
+ ...routeParamList[action.payload.name],
743
+ ...action.payload.params,
744
+ }
745
+ : action.payload.params,
746
+ }),
747
+ };
748
+ }
749
+ }
750
+ case 'RETAIN': {
751
+ const index =
752
+ action.target === state.key && action.source
753
+ ? state.routes.findIndex((r) => r.key === action.source)
754
+ : state.index;
755
+
756
+ if (index === -1) {
757
+ return null;
758
+ }
759
+
760
+ const route = state.routes[index];
761
+
762
+ const routes = state.routes.filter((r) => r !== route);
763
+ return {
764
+ ...state,
765
+ index: routes.length - 1,
766
+ routes,
767
+ preloadedRoutes: state.preloadedRoutes.concat(route),
768
+ };
769
+ }
770
+
771
+ case 'REMOVE': {
772
+ const getId = options.routeGetIdList[action.payload.name];
773
+ const id = getId?.({ params: action.payload.params });
774
+
775
+ return {
776
+ ...state,
777
+ preloadedRoutes: state.preloadedRoutes.filter(
778
+ (route) =>
779
+ route.name !== action.payload.name ||
780
+ id !== getId?.({ params: route.params })
781
+ ),
782
+ };
783
+ }
609
784
  default:
610
785
  return BaseRouter.getStateForAction(state, action);
611
786
  }
package/src/TabRouter.tsx CHANGED
@@ -41,6 +41,10 @@ export type TabNavigationState<ParamList extends ParamListBase> = Omit<
41
41
  * List of previously visited route keys.
42
42
  */
43
43
  history: { type: 'route'; key: string }[];
44
+ /**
45
+ * List of routes' key, which are supposed to be preloaded before navigating to.
46
+ */
47
+ preloadedRouteKeys: string[];
44
48
  };
45
49
 
46
50
  export type TabActionHelpers<ParamList extends ParamListBase> = {
@@ -178,6 +182,7 @@ export function TabRouter({
178
182
  routeNames,
179
183
  history,
180
184
  routes,
185
+ preloadedRouteKeys: [],
181
186
  };
182
187
  },
183
188
 
@@ -207,8 +212,8 @@ export function TabRouter({
207
212
  ...(route ? route.params : undefined),
208
213
  }
209
214
  : route
210
- ? route.params
211
- : undefined,
215
+ ? route.params
216
+ : undefined,
212
217
  } as Route<string>;
213
218
  });
214
219
 
@@ -217,9 +222,10 @@ export function TabRouter({
217
222
  routes.length - 1
218
223
  );
219
224
 
225
+ const routeKeys = routes.map((route) => route.key);
226
+
220
227
  const history =
221
- state.history?.filter((it) => routes.find((r) => r.key === it.key)) ??
222
- [];
228
+ state.history?.filter((it) => routeKeys.includes(it.key)) ?? [];
223
229
 
224
230
  return changeIndex(
225
231
  {
@@ -230,6 +236,10 @@ export function TabRouter({
230
236
  routeNames,
231
237
  history,
232
238
  routes,
239
+ preloadedRouteKeys:
240
+ state.preloadedRouteKeys?.filter((key) =>
241
+ routeKeys.includes(key)
242
+ ) ?? [],
233
243
  },
234
244
  index,
235
245
  backBehavior,
@@ -303,7 +313,7 @@ export function TabRouter({
303
313
  return null;
304
314
  }
305
315
 
306
- return changeIndex(
316
+ const updatedState = changeIndex(
307
317
  {
308
318
  ...state,
309
319
  routes: state.routes.map((route) => {
@@ -362,6 +372,13 @@ export function TabRouter({
362
372
  backBehavior,
363
373
  initialRouteName
364
374
  );
375
+
376
+ return {
377
+ ...updatedState,
378
+ preloadedRouteKeys: updatedState.preloadedRouteKeys.filter(
379
+ (key) => key !== state.routes[updatedState.index].key
380
+ ),
381
+ };
365
382
  }
366
383
 
367
384
  case 'GO_BACK': {
@@ -380,11 +397,58 @@ export function TabRouter({
380
397
 
381
398
  return {
382
399
  ...state,
400
+ preloadedRouteKeys: state.preloadedRouteKeys.filter(
401
+ (key) => key !== state.routes[index].key
402
+ ),
383
403
  history: state.history.slice(0, -1),
384
404
  index,
385
405
  };
386
406
  }
387
407
 
408
+ case 'PRELOAD': {
409
+ const routeIndex = state.routes.findIndex(
410
+ (route) => route.name === action.payload.name
411
+ );
412
+ if (routeIndex === -1) {
413
+ return null;
414
+ }
415
+ const route = state.routes[routeIndex];
416
+
417
+ const getId = routeGetIdList[route.name];
418
+
419
+ const currentId = getId?.({ params: route.params });
420
+ const nextId = getId?.({ params: action.payload.params });
421
+
422
+ const key =
423
+ currentId === nextId ? route.key : `${route.name}-${nanoid()}`;
424
+
425
+ const params =
426
+ action.payload.params !== undefined ||
427
+ routeParamList[route.name] !== undefined
428
+ ? {
429
+ ...routeParamList[route.name],
430
+ ...action.payload.params,
431
+ }
432
+ : undefined;
433
+
434
+ const newRoute =
435
+ params !== route.params ? { ...route, key, params } : route;
436
+
437
+ return {
438
+ ...state,
439
+ preloadedRouteKeys: state.preloadedRouteKeys
440
+ .filter((key) => key !== route.key)
441
+ .concat(newRoute.key),
442
+ routes: state.routes.map((route, index) =>
443
+ index === routeIndex ? newRoute : route
444
+ ),
445
+ history:
446
+ key === route.key
447
+ ? state.history
448
+ : state.history.filter((record) => record.key !== route.key),
449
+ };
450
+ }
451
+
388
452
  default:
389
453
  return BaseRouter.getStateForAction(state, action);
390
454
  }
package/src/types.tsx CHANGED
@@ -2,7 +2,7 @@ import type * as CommonActions from './CommonActions';
2
2
 
3
3
  export type CommonNavigationAction = CommonActions.Action;
4
4
 
5
- type NavigationRoute<
5
+ export type NavigationRoute<
6
6
  ParamList extends ParamListBase,
7
7
  RouteName extends keyof ParamList,
8
8
  > = Route<Extract<RouteName, string>, ParamList[RouteName]> & {