@react-navigation/routers 8.0.0-alpha.2 → 8.0.0-alpha.20

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 (42) hide show
  1. package/lib/module/BaseRouter.js +64 -61
  2. package/lib/module/BaseRouter.js.map +1 -1
  3. package/lib/module/CommonActions.js +9 -21
  4. package/lib/module/CommonActions.js.map +1 -1
  5. package/lib/module/DrawerRouter.js +15 -14
  6. package/lib/module/DrawerRouter.js.map +1 -1
  7. package/lib/module/StackRouter.js +287 -166
  8. package/lib/module/StackRouter.js.map +1 -1
  9. package/lib/module/SwitchRouter.js +426 -0
  10. package/lib/module/SwitchRouter.js.map +1 -0
  11. package/lib/module/TabRouter.js +14 -290
  12. package/lib/module/TabRouter.js.map +1 -1
  13. package/lib/typescript/src/BaseRouter.d.ts +10 -8
  14. package/lib/typescript/src/BaseRouter.d.ts.map +1 -1
  15. package/lib/typescript/src/CommonActions.d.ts +42 -39
  16. package/lib/typescript/src/CommonActions.d.ts.map +1 -1
  17. package/lib/typescript/src/DrawerRouter.d.ts +6 -5
  18. package/lib/typescript/src/DrawerRouter.d.ts.map +1 -1
  19. package/lib/typescript/src/StackRouter.d.ts +70 -24
  20. package/lib/typescript/src/StackRouter.d.ts.map +1 -1
  21. package/lib/typescript/src/SwitchRouter.d.ts +69 -0
  22. package/lib/typescript/src/SwitchRouter.d.ts.map +1 -0
  23. package/lib/typescript/src/TabRouter.d.ts +4 -24
  24. package/lib/typescript/src/TabRouter.d.ts.map +1 -1
  25. package/lib/typescript/src/createParamsFromAction.d.ts +2 -2
  26. package/lib/typescript/src/createParamsFromAction.d.ts.map +1 -1
  27. package/lib/typescript/src/createRouteFromAction.d.ts +2 -2
  28. package/lib/typescript/src/createRouteFromAction.d.ts.map +1 -1
  29. package/lib/typescript/src/index.d.ts +9 -9
  30. package/lib/typescript/src/index.d.ts.map +1 -1
  31. package/lib/typescript/src/types.d.ts +20 -30
  32. package/lib/typescript/src/types.d.ts.map +1 -1
  33. package/package.json +8 -8
  34. package/src/BaseRouter.tsx +93 -80
  35. package/src/CommonActions.tsx +44 -66
  36. package/src/DrawerRouter.tsx +28 -22
  37. package/src/StackRouter.tsx +551 -238
  38. package/src/SwitchRouter.tsx +718 -0
  39. package/src/TabRouter.tsx +24 -463
  40. package/src/createParamsFromAction.tsx +1 -1
  41. package/src/createRouteFromAction.tsx +1 -1
  42. package/src/types.tsx +39 -41
@@ -0,0 +1,718 @@
1
+ import { nanoid } from 'nanoid/non-secure';
2
+
3
+ import { BaseRouter } from './BaseRouter';
4
+ import { createParamsFromAction } from './createParamsFromAction';
5
+ import type {
6
+ CommonNavigationAction,
7
+ DefaultRouterOptions,
8
+ NavigationState,
9
+ PartialState,
10
+ Route,
11
+ RouterConfigOptions,
12
+ } from './types';
13
+
14
+ export type SwitchActionType = {
15
+ type: 'JUMP_TO';
16
+ payload: { name: string; params?: object | undefined };
17
+ source?: string | undefined;
18
+ target?: string | undefined;
19
+ };
20
+
21
+ export type SwitchRouterOptions = DefaultRouterOptions & {
22
+ /**
23
+ * Control how going back should behave
24
+ * - `firstRoute` - return to the first defined route
25
+ * - `initialRoute` - return to the route from `initialRouteName`
26
+ * - `order` - return to the route defined before the focused route
27
+ * - `history` - return to last visited route; if the same route is visited multiple times, the older entries are dropped from the history
28
+ * - `fullHistory` - return to last visited route; doesn't drop duplicate entries unlike `history` - matches behavior of web pages
29
+ * - `none` - do not handle going back
30
+ */
31
+ backBehavior?: BackBehavior | undefined;
32
+ };
33
+
34
+ type BackBehavior =
35
+ | 'firstRoute'
36
+ | 'initialRoute'
37
+ | 'order'
38
+ | 'history'
39
+ | 'fullHistory'
40
+ | 'none';
41
+
42
+ type RouteHistory = {
43
+ type: 'route';
44
+ key: string;
45
+ params?: object | undefined;
46
+ };
47
+
48
+ type DrawerHistory = {
49
+ type: 'drawer';
50
+ status: 'open' | 'closed';
51
+ };
52
+
53
+ type SwitchRouterState<History> = Omit<NavigationState, 'history' | 'type'> & {
54
+ history: History[];
55
+ preloadedRouteKeys: string[];
56
+ };
57
+
58
+ type SwitchRouterStateMap = {
59
+ tab: SwitchRouterState<RouteHistory> & { type: 'tab' };
60
+ drawer: SwitchRouterState<RouteHistory | DrawerHistory> & {
61
+ type: 'drawer';
62
+ default: DrawerHistory['status'];
63
+ };
64
+ };
65
+
66
+ type SwitchRouterType = 'tab' | 'drawer';
67
+
68
+ const TYPE_ROUTE = 'route' as const;
69
+
70
+ const getRouteHistory = (
71
+ routes: Route<string>[],
72
+ index: number,
73
+ backBehavior: BackBehavior,
74
+ initialRouteName: string | undefined
75
+ ) => {
76
+ const route = routes[index];
77
+
78
+ if (route == null) {
79
+ throw new Error(`Couldn't find a route at index ${index}.`);
80
+ }
81
+
82
+ const history: RouteHistory[] = [
83
+ {
84
+ type: TYPE_ROUTE,
85
+ key: route.key,
86
+ params: backBehavior === 'fullHistory' ? route.params : undefined,
87
+ },
88
+ ];
89
+
90
+ let initialRouteIndex;
91
+
92
+ switch (backBehavior) {
93
+ case 'order':
94
+ for (let i = index; i > 0; i--) {
95
+ const route = routes[i - 1];
96
+
97
+ if (route == null) {
98
+ throw new Error(`Couldn't find a route at index ${i - 1}.`);
99
+ }
100
+
101
+ history.unshift({
102
+ type: TYPE_ROUTE,
103
+ key: route.key,
104
+ });
105
+ }
106
+
107
+ break;
108
+
109
+ case 'firstRoute':
110
+ if (index !== 0) {
111
+ const route = routes[0];
112
+
113
+ if (route == null) {
114
+ throw new Error("Couldn't find a route at index 0.");
115
+ }
116
+
117
+ history.unshift({
118
+ type: TYPE_ROUTE,
119
+ key: route.key,
120
+ });
121
+ }
122
+
123
+ break;
124
+
125
+ case 'initialRoute':
126
+ initialRouteIndex = routes.findIndex(
127
+ (route) => route.name === initialRouteName
128
+ );
129
+
130
+ initialRouteIndex = initialRouteIndex === -1 ? 0 : initialRouteIndex;
131
+
132
+ if (index !== initialRouteIndex) {
133
+ const route = routes[initialRouteIndex];
134
+
135
+ if (route == null) {
136
+ throw new Error(
137
+ `Couldn't find a route at index ${initialRouteIndex}.`
138
+ );
139
+ }
140
+
141
+ history.unshift({
142
+ type: TYPE_ROUTE,
143
+ key: route.key,
144
+ });
145
+ }
146
+
147
+ break;
148
+
149
+ case 'history':
150
+ case 'fullHistory':
151
+ // The history will fill up on navigation
152
+ break;
153
+ }
154
+
155
+ return history;
156
+ };
157
+
158
+ const changeIndex = <Type extends SwitchRouterType>(
159
+ state: Pick<SwitchRouterStateMap[Type], 'routes' | 'history'>,
160
+ index: number,
161
+ backBehavior: BackBehavior,
162
+ initialRouteName: string | undefined
163
+ ): Pick<SwitchRouterStateMap[Type], 'index' | 'history'> => {
164
+ let history = state.history;
165
+
166
+ if (backBehavior === 'history' || backBehavior === 'fullHistory') {
167
+ const currentRoute = state.routes[index];
168
+
169
+ if (currentRoute == null) {
170
+ throw new Error(`Couldn't find a route at index ${index}.`);
171
+ }
172
+
173
+ if (backBehavior === 'history') {
174
+ // Remove the existing key from the history to de-duplicate it
175
+ history = history.filter((it) =>
176
+ it.type === 'route' ? it.key !== currentRoute.key : true
177
+ );
178
+ } else if (backBehavior === 'fullHistory') {
179
+ const lastHistoryRouteItemIndex = history.findLastIndex(
180
+ (item) => item.type === 'route'
181
+ );
182
+
183
+ const lastHistoryRouteItem = history[lastHistoryRouteItemIndex];
184
+
185
+ if (
186
+ lastHistoryRouteItem?.type === 'route' &&
187
+ currentRoute.key !== lastHistoryRouteItem.key
188
+ ) {
189
+ const previousRoute = state.routes.find(
190
+ (route) => route.key === lastHistoryRouteItem.key
191
+ );
192
+
193
+ if (
194
+ previousRoute != null &&
195
+ previousRoute.params !== lastHistoryRouteItem.params
196
+ ) {
197
+ history = [...history];
198
+ history[lastHistoryRouteItemIndex] = {
199
+ ...lastHistoryRouteItem,
200
+ params: previousRoute.params,
201
+ };
202
+ }
203
+ }
204
+
205
+ if (
206
+ lastHistoryRouteItem?.type === 'route' &&
207
+ currentRoute.key === lastHistoryRouteItem.key
208
+ ) {
209
+ // For full-history, only remove if it matches the last route
210
+ // Useful for drawer, if current route was in history, then drawer state changed
211
+ // Then we only need to move the route to the front
212
+ history = [
213
+ ...history.slice(0, lastHistoryRouteItemIndex),
214
+ ...history.slice(lastHistoryRouteItemIndex + 1),
215
+ ];
216
+ }
217
+ }
218
+
219
+ history = history.concat({
220
+ type: TYPE_ROUTE,
221
+ key: currentRoute.key,
222
+ params: backBehavior === 'fullHistory' ? currentRoute.params : undefined,
223
+ });
224
+ } else {
225
+ // Preserve non-route entries (e.g. drawer status) when rebuilding the history
226
+ history = [
227
+ ...state.history.filter((it) => it.type !== 'route'),
228
+ ...getRouteHistory(state.routes, index, backBehavior, initialRouteName),
229
+ ];
230
+ }
231
+
232
+ return {
233
+ index,
234
+ history,
235
+ };
236
+ };
237
+
238
+ export function SwitchRouter<Type extends SwitchRouterType>({
239
+ initialRouteName,
240
+ backBehavior = 'firstRoute',
241
+ }: SwitchRouterOptions) {
242
+ type State = SwitchRouterStateMap[Type];
243
+
244
+ const router = {
245
+ ...BaseRouter,
246
+
247
+ getInitialState({
248
+ routeNames,
249
+ routeParamList,
250
+ }: RouterConfigOptions): SwitchRouterState<RouteHistory> {
251
+ const index =
252
+ initialRouteName !== undefined && routeNames.includes(initialRouteName)
253
+ ? routeNames.indexOf(initialRouteName)
254
+ : 0;
255
+
256
+ const routes = routeNames.map((name) => ({
257
+ name,
258
+ key: `${name}-${nanoid()}`,
259
+ params: routeParamList[name],
260
+ }));
261
+
262
+ return {
263
+ stale: false,
264
+ key: nanoid(),
265
+ index,
266
+ routeNames,
267
+ history: getRouteHistory(routes, index, backBehavior, initialRouteName),
268
+ routes,
269
+ preloadedRouteKeys: [],
270
+ };
271
+ },
272
+
273
+ getRehydratedState(
274
+ state: PartialState<State>,
275
+ { routeNames, routeParamList }: RouterConfigOptions
276
+ ): SwitchRouterState<RouteHistory> {
277
+ const routes = routeNames.map((name) => {
278
+ const route = state.routes.find((r) => r.name === name);
279
+
280
+ return {
281
+ ...route,
282
+ name,
283
+ key:
284
+ route && route.name === name && route.key
285
+ ? route.key
286
+ : `${name}-${nanoid()}`,
287
+ params:
288
+ routeParamList[name] !== undefined
289
+ ? {
290
+ ...routeParamList[name],
291
+ ...(route ? route.params : undefined),
292
+ }
293
+ : route
294
+ ? route.params
295
+ : undefined,
296
+ } satisfies Route<string>;
297
+ });
298
+
299
+ const currentRoute = state.routes[state.index ?? 0];
300
+ const index = Math.min(
301
+ Math.max(currentRoute ? routeNames.indexOf(currentRoute.name) : 0, 0),
302
+ routes.length - 1
303
+ );
304
+
305
+ const routeKeys = routes.map((route) => route.key);
306
+ const history: RouteHistory[] = [];
307
+
308
+ for (const item of state.history ?? []) {
309
+ if (item.type === 'route' && routeKeys.includes(item.key)) {
310
+ history.push(item);
311
+ }
312
+ }
313
+
314
+ const stateData: SwitchRouterState<RouteHistory> = {
315
+ stale: false,
316
+ key: nanoid(),
317
+ index,
318
+ routeNames,
319
+ history,
320
+ routes,
321
+ preloadedRouteKeys:
322
+ state.preloadedRouteKeys?.filter((key) => routeKeys.includes(key)) ??
323
+ [],
324
+ };
325
+
326
+ return {
327
+ ...stateData,
328
+ ...changeIndex<'tab'>(stateData, index, backBehavior, initialRouteName),
329
+ };
330
+ },
331
+
332
+ getStateForRouteNamesChange(
333
+ state: State,
334
+ {
335
+ routeNames,
336
+ routeParamList,
337
+ routeKeyChanges,
338
+ }: RouterConfigOptions & { routeKeyChanges: string[] }
339
+ ): State {
340
+ const routes = routeNames.map(
341
+ (name) =>
342
+ state.routes.find(
343
+ (r) => r.name === name && !routeKeyChanges.includes(r.name)
344
+ ) ?? {
345
+ name,
346
+ key: `${name}-${nanoid()}`,
347
+ params: routeParamList[name],
348
+ }
349
+ );
350
+
351
+ const routeKeys = new Set(routes.map((route) => route.key));
352
+
353
+ const currentRoute = state.routes[state.index];
354
+
355
+ if (currentRoute == null) {
356
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
357
+ }
358
+
359
+ const history = state.history.filter(
360
+ // Type will always be 'route' for tabs, but could be different in a router extending this (e.g. drawer)
361
+ (item) => item.type !== 'route' || routeKeys.has(item.key)
362
+ );
363
+
364
+ let index = routeNames.indexOf(currentRoute.name);
365
+
366
+ if (index === -1) {
367
+ // The focused route was removed, so focus the most recently visited surviving route
368
+ const previousRouteItem = history.findLast(
369
+ (item): item is RouteHistory => item.type === 'route'
370
+ );
371
+
372
+ index = Math.max(
373
+ 0,
374
+ routes.findIndex((route) => route.key === previousRouteItem?.key)
375
+ );
376
+ }
377
+
378
+ return {
379
+ ...state,
380
+ routeNames,
381
+ routes,
382
+ ...changeIndex<Type>(
383
+ { routes, history },
384
+ index,
385
+ backBehavior,
386
+ initialRouteName
387
+ ),
388
+ preloadedRouteKeys: state.preloadedRouteKeys.filter((key) =>
389
+ routeKeys.has(key)
390
+ ),
391
+ };
392
+ },
393
+
394
+ getStateForRouteFocus(state: State, key: string): State {
395
+ const index = state.routes.findIndex((r) => r.key === key);
396
+
397
+ if (index === -1 || index === state.index) {
398
+ return state;
399
+ }
400
+
401
+ return {
402
+ ...state,
403
+ ...changeIndex<Type>(state, index, backBehavior, initialRouteName),
404
+ };
405
+ },
406
+
407
+ getStateForAction(
408
+ state: State,
409
+ action: SwitchActionType | CommonNavigationAction,
410
+ { routeParamList, routeGetIdList }: RouterConfigOptions
411
+ ): State | PartialState<State> | null {
412
+ switch (action.type) {
413
+ case 'JUMP_TO':
414
+ case 'NAVIGATE': {
415
+ const index = state.routes.findIndex(
416
+ (route) => route.name === action.payload.name
417
+ );
418
+
419
+ if (index === -1) {
420
+ return null;
421
+ }
422
+
423
+ if (action.source !== undefined) {
424
+ const sourceIndex = state.routes.findIndex(
425
+ (route) => route.key === action.source
426
+ );
427
+
428
+ if (sourceIndex === -1) {
429
+ return null;
430
+ }
431
+
432
+ if (backBehavior === 'history' || backBehavior === 'fullHistory') {
433
+ const sourceHistoryIndex = state.history.findLastIndex(
434
+ (item) => item.type === 'route' && item.key === action.source
435
+ );
436
+
437
+ if (sourceHistoryIndex !== -1) {
438
+ const history = state.history.filter(
439
+ (item, index) =>
440
+ item.type !== 'route' || index <= sourceHistoryIndex
441
+ );
442
+
443
+ state = {
444
+ ...state,
445
+ ...changeIndex<Type>(
446
+ { routes: state.routes, history },
447
+ sourceIndex,
448
+ backBehavior,
449
+ initialRouteName
450
+ ),
451
+ };
452
+ }
453
+ }
454
+ }
455
+
456
+ const route = state.routes[index];
457
+
458
+ if (route == null) {
459
+ throw new Error(`Couldn't find a route at index ${index}.`);
460
+ }
461
+
462
+ const getId = routeGetIdList[route.name];
463
+
464
+ const currentId = getId?.({ params: route.params });
465
+ const nextId = getId?.({ params: action.payload.params });
466
+
467
+ const key =
468
+ currentId === nextId ? route.key : `${route.name}-${nanoid()}`;
469
+
470
+ let params;
471
+
472
+ if (
473
+ action.type === 'NAVIGATE' &&
474
+ action.payload.merge &&
475
+ currentId === nextId
476
+ ) {
477
+ params =
478
+ action.payload.params !== undefined ||
479
+ routeParamList[route.name] !== undefined
480
+ ? {
481
+ ...routeParamList[route.name],
482
+ ...route.params,
483
+ ...action.payload.params,
484
+ }
485
+ : route.params;
486
+ } else {
487
+ params = createParamsFromAction({ action, routeParamList });
488
+ }
489
+
490
+ const path =
491
+ action.type === 'NAVIGATE' && action.payload.path != null
492
+ ? action.payload.path
493
+ : key === route.key
494
+ ? route.path
495
+ : undefined;
496
+
497
+ const nextRoute =
498
+ key !== route.key
499
+ ? { key, name: route.name, path, params }
500
+ : params !== route.params || path !== route.path
501
+ ? { ...route, path, params }
502
+ : route;
503
+
504
+ const routes = state.routes.map((item, routeIndex) =>
505
+ routeIndex === index ? nextRoute : item
506
+ );
507
+
508
+ const updatedState: State = {
509
+ ...state,
510
+ routes,
511
+ history:
512
+ key === route.key
513
+ ? state.history
514
+ : state.history.filter(
515
+ (it) => it.type !== 'route' || it.key !== route.key
516
+ ),
517
+ };
518
+
519
+ return {
520
+ ...updatedState,
521
+ ...changeIndex<Type>(
522
+ updatedState,
523
+ index,
524
+ backBehavior,
525
+ initialRouteName
526
+ ),
527
+ preloadedRouteKeys: updatedState.preloadedRouteKeys.filter(
528
+ (key) => key !== route.key && key !== nextRoute.key
529
+ ),
530
+ };
531
+ }
532
+
533
+ case 'GO_BACK': {
534
+ if (action.source !== undefined) {
535
+ const sourceHistoryIndex = state.history.findLastIndex(
536
+ (item) => item.type === 'route' && item.key === action.source
537
+ );
538
+
539
+ if (sourceHistoryIndex === -1) {
540
+ // The source screen was never visited,
541
+ // so there's nothing before it to go back to
542
+ return null;
543
+ }
544
+
545
+ const sourceIndex = state.routes.findIndex(
546
+ (route) => route.key === action.source
547
+ );
548
+
549
+ // Remove the routes visited after the source from the history
550
+ // Since we're going back from the source route
551
+ const history = state.history.filter(
552
+ (item, index) =>
553
+ item.type !== 'route' || index <= sourceHistoryIndex
554
+ );
555
+
556
+ state = {
557
+ ...state,
558
+ ...changeIndex<Type>(
559
+ { routes: state.routes, history },
560
+ sourceIndex,
561
+ backBehavior,
562
+ initialRouteName
563
+ ),
564
+ };
565
+ }
566
+
567
+ const focusedRoute = state.routes[state.index];
568
+
569
+ if (focusedRoute == null) {
570
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
571
+ }
572
+
573
+ if (state.history.length === 1 && !focusedRoute.history?.length) {
574
+ return null;
575
+ }
576
+
577
+ const lastHistoryItem = state.history[state.history.length - 1];
578
+
579
+ if (
580
+ lastHistoryItem?.type === 'route' &&
581
+ focusedRoute.history?.length
582
+ ) {
583
+ const routes = [...state.routes];
584
+ const history = [...focusedRoute.history];
585
+ const last = history.pop();
586
+
587
+ routes[state.index] = {
588
+ ...focusedRoute,
589
+ params:
590
+ last?.type === 'params' ? last.params : focusedRoute.params,
591
+ history,
592
+ };
593
+
594
+ return {
595
+ ...state,
596
+ routes,
597
+ preloadedRouteKeys: state.preloadedRouteKeys.filter(
598
+ (key) => key !== focusedRoute.key
599
+ ),
600
+ };
601
+ }
602
+
603
+ const previousHistoryItem = state.history[state.history.length - 2];
604
+
605
+ if (
606
+ previousHistoryItem == null ||
607
+ previousHistoryItem.type !== 'route'
608
+ ) {
609
+ return null;
610
+ }
611
+
612
+ const previousKey = previousHistoryItem.key;
613
+ const index = state.routes.findLastIndex(
614
+ (route) => route.key === previousKey
615
+ );
616
+
617
+ if (index === -1) {
618
+ return null;
619
+ }
620
+
621
+ let routes = state.routes;
622
+
623
+ const previousRoute = routes[index];
624
+
625
+ if (previousRoute == null) {
626
+ throw new Error(`Couldn't find a route at index ${index}.`);
627
+ }
628
+
629
+ if (
630
+ backBehavior === 'fullHistory' &&
631
+ previousRoute.params !== previousHistoryItem.params
632
+ ) {
633
+ routes = [...state.routes];
634
+ routes[index] = {
635
+ ...previousRoute,
636
+ params: previousHistoryItem.params,
637
+ };
638
+ }
639
+
640
+ return {
641
+ ...state,
642
+ routes,
643
+ preloadedRouteKeys: state.preloadedRouteKeys.filter(
644
+ (key) => key !== previousRoute.key
645
+ ),
646
+ history: state.history.slice(0, -1),
647
+ index,
648
+ };
649
+ }
650
+
651
+ case 'PRELOAD': {
652
+ const routeIndex = state.routes.findIndex(
653
+ (route) => route.name === action.payload.name
654
+ );
655
+
656
+ if (routeIndex === -1) {
657
+ return null;
658
+ }
659
+
660
+ const route = state.routes[routeIndex];
661
+
662
+ if (route == null) {
663
+ throw new Error(`Couldn't find a route at index ${routeIndex}.`);
664
+ }
665
+
666
+ const getId = routeGetIdList[route.name];
667
+
668
+ const currentId = getId?.({ params: route.params });
669
+ const nextId = getId?.({ params: action.payload.params });
670
+
671
+ const key =
672
+ currentId === nextId ? route.key : `${route.name}-${nanoid()}`;
673
+
674
+ const params = createParamsFromAction({ action, routeParamList });
675
+ const newRoute =
676
+ key !== route.key
677
+ ? { key, name: route.name, params }
678
+ : params !== route.params
679
+ ? { ...route, params }
680
+ : route;
681
+
682
+ let history = state.history;
683
+
684
+ if (key !== route.key) {
685
+ history = history.filter(
686
+ (record) => record.type !== 'route' || record.key !== route.key
687
+ );
688
+
689
+ if (routeIndex === state.index) {
690
+ history = history.concat({
691
+ type: TYPE_ROUTE,
692
+ key: newRoute.key,
693
+ params:
694
+ backBehavior === 'fullHistory' ? newRoute.params : undefined,
695
+ });
696
+ }
697
+ }
698
+
699
+ return {
700
+ ...state,
701
+ preloadedRouteKeys: state.preloadedRouteKeys
702
+ .filter((key) => key !== route.key)
703
+ .concat(newRoute.key),
704
+ routes: state.routes.map((route, index) =>
705
+ index === routeIndex ? newRoute : route
706
+ ),
707
+ history,
708
+ };
709
+ }
710
+
711
+ default:
712
+ return BaseRouter.getStateForAction(state, action);
713
+ }
714
+ },
715
+ };
716
+
717
+ return router;
718
+ }