@react-navigation/routers 8.0.0-alpha.1 → 8.0.0-alpha.11

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 (34) hide show
  1. package/lib/module/BaseRouter.js +3 -2
  2. package/lib/module/BaseRouter.js.map +1 -1
  3. package/lib/module/CommonActions.js.map +1 -1
  4. package/lib/module/StackRouter.js +334 -149
  5. package/lib/module/StackRouter.js.map +1 -1
  6. package/lib/module/TabRouter.js +52 -11
  7. package/lib/module/TabRouter.js.map +1 -1
  8. package/lib/typescript/src/BaseRouter.d.ts +7 -7
  9. package/lib/typescript/src/BaseRouter.d.ts.map +1 -1
  10. package/lib/typescript/src/CommonActions.d.ts +35 -35
  11. package/lib/typescript/src/CommonActions.d.ts.map +1 -1
  12. package/lib/typescript/src/DrawerRouter.d.ts +5 -5
  13. package/lib/typescript/src/DrawerRouter.d.ts.map +1 -1
  14. package/lib/typescript/src/StackRouter.d.ts +41 -18
  15. package/lib/typescript/src/StackRouter.d.ts.map +1 -1
  16. package/lib/typescript/src/TabRouter.d.ts +6 -6
  17. package/lib/typescript/src/TabRouter.d.ts.map +1 -1
  18. package/lib/typescript/src/createParamsFromAction.d.ts +2 -2
  19. package/lib/typescript/src/createParamsFromAction.d.ts.map +1 -1
  20. package/lib/typescript/src/createRouteFromAction.d.ts +2 -2
  21. package/lib/typescript/src/createRouteFromAction.d.ts.map +1 -1
  22. package/lib/typescript/src/index.d.ts +9 -9
  23. package/lib/typescript/src/index.d.ts.map +1 -1
  24. package/lib/typescript/src/types.d.ts +17 -27
  25. package/lib/typescript/src/types.d.ts.map +1 -1
  26. package/package.json +7 -7
  27. package/src/BaseRouter.tsx +4 -4
  28. package/src/CommonActions.tsx +36 -34
  29. package/src/DrawerRouter.tsx +3 -3
  30. package/src/StackRouter.tsx +546 -187
  31. package/src/TabRouter.tsx +79 -18
  32. package/src/createParamsFromAction.tsx +1 -1
  33. package/src/createRouteFromAction.tsx +1 -1
  34. package/src/types.tsx +30 -39
@@ -45,8 +45,50 @@ export const StackActions = {
45
45
  merge: options?.merge
46
46
  }
47
47
  };
48
+ },
49
+ retain(enable) {
50
+ return {
51
+ type: 'RETAIN',
52
+ payload: {
53
+ enable
54
+ }
55
+ };
48
56
  }
49
57
  };
58
+ function retainRoutes(previousState, nextState) {
59
+ if (!nextState.retainedRouteKeys.length) {
60
+ return nextState;
61
+ }
62
+ const retainedRouteKeySet = new Set(nextState.retainedRouteKeys);
63
+ const nextRouteKeySet = new Set(nextState.routes.map(route => route.key));
64
+ const retainedRoutes = getActiveRoutes(previousState).filter(route => retainedRouteKeySet.has(route.key) && !nextRouteKeySet.has(route.key));
65
+ if (!retainedRoutes.length) {
66
+ return nextState;
67
+ }
68
+ const removedRetainedRouteKeys = new Set(retainedRoutes.map(route => route.key));
69
+ return getStateWithRoutes(nextState, getActiveRoutes(nextState), getPreloadedRoutes(nextState), retainedRoutes.concat(getRetainedRoutes(nextState).filter(route => !removedRetainedRouteKeys.has(route.key))));
70
+ }
71
+ const getActiveRoutes = state => state.routes.slice(0, state.index + 1);
72
+ const getInactiveRoutes = state => state.routes.slice(state.index + 1);
73
+ const getPreloadedRoutes = state => {
74
+ const retainedRouteKeySet = new Set(state.retainedRouteKeys);
75
+ return getInactiveRoutes(state).filter(route => !retainedRouteKeySet.has(route.key));
76
+ };
77
+ const getRetainedRoutes = state => {
78
+ const retainedRouteKeySet = new Set(state.retainedRouteKeys);
79
+ return getInactiveRoutes(state).filter(route => retainedRouteKeySet.has(route.key));
80
+ };
81
+ const getStateWithRoutes = (state, activeRoutes, preloadedRoutes = getPreloadedRoutes(state), retainedRoutes = getRetainedRoutes(state)) => {
82
+ const routeKeySet = new Set(activeRoutes.map(route => route.key));
83
+ const retainedRouteKeySet = new Set(state.retainedRouteKeys);
84
+ const filteredRetainedRoutes = retainedRoutes.filter(route => !routeKeySet.has(route.key));
85
+ const filteredPreloadedRoutes = preloadedRoutes.filter(route => !retainedRouteKeySet.has(route.key) && !routeKeySet.has(route.key));
86
+ return {
87
+ ...state,
88
+ index: activeRoutes.length - 1,
89
+ routes: activeRoutes.concat(filteredRetainedRoutes, filteredPreloadedRoutes)
90
+ };
91
+ };
50
92
  export function StackRouter(options) {
51
93
  const router = {
52
94
  ...BaseRouter,
@@ -56,13 +98,16 @@ export function StackRouter(options) {
56
98
  routeParamList
57
99
  }) {
58
100
  const initialRouteName = options.initialRouteName !== undefined && routeNames.includes(options.initialRouteName) ? options.initialRouteName : routeNames[0];
101
+ if (initialRouteName == null) {
102
+ throw new Error("Couldn't initialize the stack navigator. Have you defined any screens?");
103
+ }
59
104
  return {
60
105
  stale: false,
61
106
  type: 'stack',
62
107
  key: `stack-${nanoid()}`,
63
108
  index: 0,
64
109
  routeNames,
65
- preloadedRoutes: [],
110
+ retainedRouteKeys: [],
66
111
  routes: [{
67
112
  key: `${initialRouteName}-${nanoid()}`,
68
113
  name: initialRouteName,
@@ -86,30 +131,28 @@ export function StackRouter(options) {
86
131
  ...route.params
87
132
  } : route.params
88
133
  }));
89
- const preloadedRoutes = state.preloadedRoutes?.filter(route => routeNames.includes(route.name)).map(route => ({
90
- ...route,
91
- key: route.key || `${route.name}-${nanoid()}`,
92
- params: routeParamList[route.name] !== undefined ? {
93
- ...routeParamList[route.name],
94
- ...route.params
95
- } : route.params
96
- })) ?? [];
97
134
  if (routes.length === 0) {
98
135
  const initialRouteName = options.initialRouteName !== undefined ? options.initialRouteName : routeNames[0];
136
+ if (initialRouteName == null) {
137
+ throw new Error("Couldn't initialize the stack navigator. Have you defined any screens?");
138
+ }
99
139
  routes.push({
100
140
  key: `${initialRouteName}-${nanoid()}`,
101
141
  name: initialRouteName,
102
142
  params: routeParamList[initialRouteName]
103
143
  });
104
144
  }
145
+ const index = Math.min(Math.max(state.index ?? routes.length - 1, 0), routes.length - 1);
146
+ const routeKeys = new Set(routes.map(route => route.key));
147
+ const retainedRouteKeys = state.retainedRouteKeys?.filter(key => routeKeys.has(key)) ?? [];
105
148
  return {
106
149
  stale: false,
107
150
  type: 'stack',
108
151
  key: `stack-${nanoid()}`,
109
- index: routes.length - 1,
152
+ index,
110
153
  routeNames,
111
154
  routes,
112
- preloadedRoutes
155
+ retainedRouteKeys
113
156
  };
114
157
  },
115
158
  getStateForRouteNamesChange(state, {
@@ -117,41 +160,47 @@ export function StackRouter(options) {
117
160
  routeParamList,
118
161
  routeKeyChanges
119
162
  }) {
120
- const routes = state.routes.filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
163
+ const routes = getActiveRoutes(state).filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
164
+ const preloadedRoutes = getPreloadedRoutes(state).filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
165
+ const retainedRoutes = getRetainedRoutes(state).filter(route => routeNames.includes(route.name) && !routeKeyChanges.includes(route.name));
121
166
  if (routes.length === 0) {
122
167
  const initialRouteName = options.initialRouteName !== undefined && routeNames.includes(options.initialRouteName) ? options.initialRouteName : routeNames[0];
168
+ if (initialRouteName == null) {
169
+ throw new Error("Couldn't initialize the stack navigator. Have you defined any screens?");
170
+ }
123
171
  routes.push({
124
172
  key: `${initialRouteName}-${nanoid()}`,
125
173
  name: initialRouteName,
126
174
  params: routeParamList[initialRouteName]
127
175
  });
128
176
  }
129
- return {
177
+ const routeKeys = new Set([...routes, ...preloadedRoutes, ...retainedRoutes].map(route => route.key));
178
+ const retainedRouteKeys = state.retainedRouteKeys.filter(key => routeKeys.has(key));
179
+ return getStateWithRoutes({
130
180
  ...state,
131
181
  routeNames,
132
- routes,
133
- index: Math.min(state.index, routes.length - 1)
134
- };
182
+ retainedRouteKeys
183
+ }, routes, preloadedRoutes, retainedRoutes);
135
184
  },
136
185
  getStateForRouteFocus(state, key) {
137
- const index = state.routes.findIndex(r => r.key === key);
186
+ const routes = getActiveRoutes(state);
187
+ const index = routes.findIndex(r => r.key === key);
138
188
  if (index === -1 || index === state.index) {
139
189
  return state;
140
190
  }
141
- return {
142
- ...state,
143
- index,
144
- routes: state.routes.slice(0, index + 1)
145
- };
191
+ return retainRoutes(state, getStateWithRoutes(state, routes.slice(0, index + 1)));
146
192
  },
147
193
  getStateForAction(state, action, options) {
148
194
  const {
149
195
  routeParamList
150
196
  } = options;
197
+ const routes = getActiveRoutes(state);
198
+ const preloadedRoutes = getPreloadedRoutes(state);
199
+ const inactiveRoutes = getInactiveRoutes(state);
151
200
  switch (action.type) {
152
201
  case 'REPLACE':
153
202
  {
154
- const currentIndex = action.target === state.key && action.source ? state.routes.findIndex(r => r.key === action.source) : state.index;
203
+ const currentIndex = action.target === state.key && action.source ? routes.findIndex(r => r.key === action.source) : state.index;
155
204
  if (currentIndex === -1) {
156
205
  return null;
157
206
  }
@@ -163,8 +212,8 @@ export function StackRouter(options) {
163
212
  params: action.payload.params
164
213
  });
165
214
 
166
- // Re-use preloaded route if available
167
- let route = state.preloadedRoutes.find(route => route.name === action.payload.name && id === getId?.({
215
+ // Re-use inactive route if available
216
+ let route = inactiveRoutes.find(route => route.name === action.payload.name && id === getId?.({
168
217
  params: route.params
169
218
  }));
170
219
  if (!route) {
@@ -172,12 +221,19 @@ export function StackRouter(options) {
172
221
  action,
173
222
  routeParamList
174
223
  });
224
+ } else {
225
+ const params = createParamsFromAction({
226
+ action,
227
+ routeParamList
228
+ });
229
+ if (route.params !== params) {
230
+ route = {
231
+ ...route,
232
+ params
233
+ };
234
+ }
175
235
  }
176
- return {
177
- ...state,
178
- routes: state.routes.map((r, i) => i === currentIndex ? route : r),
179
- preloadedRoutes: state.preloadedRoutes.filter(r => r.key !== route.key)
180
- };
236
+ return retainRoutes(state, getStateWithRoutes(state, routes.map((r, i) => i === currentIndex ? route : r), preloadedRoutes.filter(r => r.key !== route.key)));
181
237
  }
182
238
  case 'PUSH':
183
239
  case 'NAVIGATE':
@@ -190,22 +246,65 @@ export function StackRouter(options) {
190
246
  params: action.payload.params
191
247
  });
192
248
  let route;
193
- if (id !== undefined) {
194
- route = state.routes.findLast(route => route.name === action.payload.name && id === getId?.({
195
- params: route.params
196
- }));
197
- } else if (action.type === 'NAVIGATE') {
198
- const currentRoute = state.routes[state.index];
199
-
200
- // If the route matches the current one, then navigate to it
201
- if (action.payload.name === currentRoute.name) {
202
- route = currentRoute;
203
- } else if (action.payload.pop) {
204
- route = state.routes.findLast(route => route.name === action.payload.name);
249
+ if (action.type === 'NAVIGATE') {
250
+ const currentRoute = routes[state.index];
251
+ if (currentRoute == null) {
252
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
253
+ }
254
+ if (id !== undefined) {
255
+ if (currentRoute.name === action.payload.name && id === getId?.({
256
+ params: currentRoute.params
257
+ })) {
258
+ route = currentRoute;
259
+ } else if (action.payload.pop) {
260
+ for (let i = routes.length - 1; i >= 0; i--) {
261
+ const r = routes[i];
262
+ if (r == null) {
263
+ throw new Error(`Couldn't find a route at index ${i}.`);
264
+ }
265
+ if (r.name !== action.payload.name) {
266
+ continue;
267
+ }
268
+ if (id === getId?.({
269
+ params: r.params
270
+ })) {
271
+ route = r;
272
+ break;
273
+ }
274
+ if (r.history?.length) {
275
+ for (let j = r.history.length - 1; j >= 0; j--) {
276
+ const historyEntry = r.history[j];
277
+ if (historyEntry == null) {
278
+ throw new Error(`Couldn't find a route history entry at index ${j}.`);
279
+ }
280
+ if (historyEntry.type === 'params' && id === getId?.({
281
+ params: historyEntry.params
282
+ })) {
283
+ route = {
284
+ ...r,
285
+ params: historyEntry.params,
286
+ history: r.history.slice(0, j)
287
+ };
288
+ break;
289
+ }
290
+ }
291
+ if (route) {
292
+ break;
293
+ }
294
+ }
295
+ }
296
+ }
297
+ } else {
298
+ // If the route matches the current one, then navigate to it
299
+ if (action.payload.name === currentRoute.name) {
300
+ route = currentRoute;
301
+ } else if (action.payload.pop) {
302
+ route = routes.findLast(route => route.name === action.payload.name);
303
+ }
205
304
  }
206
305
  }
207
306
  if (!route) {
208
- route = state.preloadedRoutes.find(route => route.name === action.payload.name && id === getId?.({
307
+ route = inactiveRoutes.find(route => route.name === action.payload.name && id === getId?.({
209
308
  params: route.params
210
309
  }));
211
310
  }
@@ -222,50 +321,62 @@ export function StackRouter(options) {
222
321
  routeParamList
223
322
  });
224
323
  }
225
- let routes;
324
+ let nextRoutes;
226
325
  if (route) {
227
326
  if (action.type === 'NAVIGATE' && action.payload.pop) {
228
- routes = [];
327
+ nextRoutes = [];
229
328
 
230
329
  // Get all routes until the matching one
231
- for (const r of state.routes) {
330
+ for (const r of routes) {
232
331
  if (r.key === route.key) {
233
- routes.push({
332
+ nextRoutes.push({
234
333
  ...route,
235
334
  path: action.payload.path !== undefined ? action.payload.path : route.path,
236
335
  params
237
336
  });
238
337
  break;
239
338
  }
240
- routes.push(r);
339
+ nextRoutes.push(r);
340
+ }
341
+ if (!nextRoutes.some(r => r.key === route.key)) {
342
+ nextRoutes.push({
343
+ ...route,
344
+ path: action.payload.path !== undefined ? action.payload.path : route.path,
345
+ params
346
+ });
241
347
  }
242
348
  } else {
243
- routes = state.routes.filter(r => r.key !== route.key);
244
- routes.push({
349
+ nextRoutes = routes.filter(r => r.key !== route.key);
350
+ nextRoutes.push({
245
351
  ...route,
246
352
  path: action.type === 'NAVIGATE' && action.payload.path !== undefined ? action.payload.path : route.path,
247
353
  params
248
354
  });
249
355
  }
250
356
  } else {
251
- routes = [...state.routes, {
357
+ nextRoutes = [...routes, {
252
358
  key: `${action.payload.name}-${nanoid()}`,
253
359
  name: action.payload.name,
254
360
  path: action.type === 'NAVIGATE' ? action.payload.path : undefined,
255
361
  params
256
362
  }];
257
363
  }
258
- return {
259
- ...state,
260
- index: routes.length - 1,
261
- preloadedRoutes: state.preloadedRoutes.filter(route => routes[routes.length - 1].key !== route.key),
262
- routes
263
- };
364
+ const lastRoute = nextRoutes[nextRoutes.length - 1];
365
+ if (lastRoute == null) {
366
+ throw new Error(`Couldn't find a route at index ${nextRoutes.length - 1}.`);
367
+ }
368
+ return retainRoutes(state, getStateWithRoutes(state, nextRoutes, preloadedRoutes.filter(route => lastRoute.key !== route.key)));
264
369
  }
265
370
  case 'POP':
266
371
  {
267
- let currentIndex = action.target === state.key && action.source ? state.routes.findIndex(r => r.key === action.source) : state.index;
268
- let route = state.routes[currentIndex];
372
+ let currentIndex = action.target === state.key && action.source ? routes.findIndex(r => r.key === action.source) : state.index;
373
+ if (currentIndex === -1) {
374
+ return null;
375
+ }
376
+ let route = routes[currentIndex];
377
+ if (route == null) {
378
+ throw new Error(`Couldn't find a route at index ${currentIndex}.`);
379
+ }
269
380
 
270
381
  /**
271
382
  * When popping entries,
@@ -278,42 +389,45 @@ export function StackRouter(options) {
278
389
  */
279
390
  if (route.history?.length || currentIndex > 0) {
280
391
  let count = action.payload.count;
281
- let routes = state.routes;
392
+ let nextRoutes = routes;
282
393
  while (count > 0 && (route.history?.length || currentIndex > 0)) {
283
394
  if (route.history?.length) {
284
- routes = [...routes];
395
+ nextRoutes = [...nextRoutes];
285
396
  const end = Math.min(count, route.history.length);
286
397
  const last = route.history.at(-end);
287
398
  const history = route.history.slice(0, -end);
288
399
  const removed = route.history.length - history.length;
289
400
  count = count - removed;
290
- routes[currentIndex] = {
401
+ nextRoutes[currentIndex] = {
291
402
  ...route,
292
403
  params: last?.type === 'params' ? last.params : route.params,
293
404
  history
294
405
  };
295
406
  }
296
407
  if (currentIndex > 0 && count > 0) {
297
- const length = routes.length;
408
+ const length = nextRoutes.length;
298
409
  const end = Math.max(currentIndex - count + 1, 1);
299
- routes = routes.slice(0, end).concat(routes.slice(currentIndex + 1));
300
- const removed = length - routes.length;
410
+ nextRoutes = nextRoutes.slice(0, end).concat(nextRoutes.slice(currentIndex + 1));
411
+ const removed = length - nextRoutes.length;
301
412
  count = count - removed;
302
- currentIndex = routes.length - 1;
303
- route = routes[currentIndex];
413
+ currentIndex = nextRoutes.length - 1;
414
+ const currentRoute = nextRoutes[currentIndex];
415
+ if (currentRoute == null) {
416
+ throw new Error(`Couldn't find a route at index ${currentIndex}.`);
417
+ }
418
+ route = currentRoute;
304
419
  }
305
420
  }
306
- return {
307
- ...state,
308
- index: routes.length - 1,
309
- routes
310
- };
421
+ return retainRoutes(state, getStateWithRoutes(state, nextRoutes));
311
422
  }
312
423
  return null;
313
424
  }
314
425
  case 'POP_TO_TOP':
315
426
  {
316
- let route = state.routes[0];
427
+ let route = routes[0];
428
+ if (route == null) {
429
+ throw new Error("Couldn't find a route at index 0.");
430
+ }
317
431
  if (state.index > 0 || route.history?.length) {
318
432
  if (route.history?.length) {
319
433
  route = {
@@ -321,17 +435,13 @@ export function StackRouter(options) {
321
435
  history: undefined
322
436
  };
323
437
  }
324
- return {
325
- ...state,
326
- index: 0,
327
- routes: [route]
328
- };
438
+ return retainRoutes(state, getStateWithRoutes(state, [route]));
329
439
  }
330
440
  return null;
331
441
  }
332
442
  case 'POP_TO':
333
443
  {
334
- const currentIndex = action.target === state.key && action.source ? state.routes.findLastIndex(r => r.key === action.source) : state.index;
444
+ const currentIndex = action.target === state.key && action.source ? routes.findLastIndex(r => r.key === action.source) : state.index;
335
445
  if (currentIndex === -1) {
336
446
  return null;
337
447
  }
@@ -341,19 +451,61 @@ export function StackRouter(options) {
341
451
 
342
452
  // If the route already exists, navigate to it
343
453
  let index = -1;
454
+ let historyIndex;
344
455
  const getId = options.routeGetIdList[action.payload.name];
345
456
  const id = getId?.({
346
457
  params: action.payload.params
347
458
  });
348
459
  if (id !== undefined) {
349
- index = state.routes.findIndex(route => route.name === action.payload.name && id === getId?.({
350
- params: route.params
351
- }));
352
- } else if (state.routes[currentIndex].name === action.payload.name) {
353
- index = currentIndex;
460
+ for (let i = currentIndex; i >= 0; i--) {
461
+ const r = routes[i];
462
+ if (r == null) {
463
+ throw new Error(`Couldn't find a route at index ${i}.`);
464
+ }
465
+ if (r.name !== action.payload.name) {
466
+ continue;
467
+ }
468
+ if (id === getId?.({
469
+ params: r.params
470
+ })) {
471
+ index = i;
472
+ break;
473
+ }
474
+ if (r.history?.length) {
475
+ for (let j = r.history.length - 1; j >= 0; j--) {
476
+ const historyEntry = r.history[j];
477
+ if (historyEntry == null) {
478
+ throw new Error(`Couldn't find a route history entry at index ${j}.`);
479
+ }
480
+ if (historyEntry.type === 'params' && id === getId?.({
481
+ params: historyEntry.params
482
+ })) {
483
+ index = i;
484
+ historyIndex = j;
485
+ break;
486
+ }
487
+ }
488
+ if (index !== -1) {
489
+ break;
490
+ }
491
+ }
492
+ }
354
493
  } else {
494
+ const currentRoute = routes[currentIndex];
495
+ if (currentRoute == null) {
496
+ throw new Error(`Couldn't find a route at index ${currentIndex}.`);
497
+ }
498
+ if (currentRoute.name === action.payload.name) {
499
+ index = currentIndex;
500
+ }
501
+ }
502
+ if (id == null && index === -1) {
355
503
  for (let i = currentIndex; i >= 0; i--) {
356
- if (state.routes[i].name === action.payload.name) {
504
+ const route = routes[i];
505
+ if (route == null) {
506
+ throw new Error(`Couldn't find a route at index ${i}.`);
507
+ }
508
+ if (route.name === action.payload.name) {
357
509
  index = i;
358
510
  break;
359
511
  }
@@ -362,8 +514,8 @@ export function StackRouter(options) {
362
514
 
363
515
  // If the route doesn't exist, remove the current route and add the new one
364
516
  if (index === -1) {
365
- // Re-use preloaded route if available
366
- let route = state.preloadedRoutes.find(route => route.name === action.payload.name && id === getId?.({
517
+ // Re-use inactive route if available
518
+ let route = inactiveRoutes.find(route => route.name === action.payload.name && id === getId?.({
367
519
  params: route.params
368
520
  }));
369
521
  if (!route) {
@@ -371,97 +523,130 @@ export function StackRouter(options) {
371
523
  action,
372
524
  routeParamList
373
525
  });
526
+ } else {
527
+ const params = createParamsFromAction({
528
+ action,
529
+ routeParamList
530
+ });
531
+ if (route.params !== params) {
532
+ route = {
533
+ ...route,
534
+ params
535
+ };
536
+ }
374
537
  }
375
- const routes = state.routes.slice(0, currentIndex).concat(route);
376
- return {
377
- ...state,
378
- index: routes.length - 1,
379
- routes,
380
- preloadedRoutes: state.preloadedRoutes.filter(r => r.key !== route.key)
381
- };
538
+ const nextRoutes = routes.slice(0, currentIndex).concat(route);
539
+ return retainRoutes(state, getStateWithRoutes(state, nextRoutes, preloadedRoutes.filter(r => r.key !== route.key)));
540
+ }
541
+ const route = routes[index];
542
+ if (route == null) {
543
+ throw new Error(`Couldn't find a route at index ${index}.`);
544
+ }
545
+ let baseParams = route.params;
546
+ let history = route.history;
547
+ if (historyIndex !== undefined && route.history) {
548
+ const historyEntry = route.history[historyIndex];
549
+ if (historyEntry == null) {
550
+ throw new Error(`Couldn't find a route history entry at index ${historyIndex}.`);
551
+ }
552
+ baseParams = historyEntry.params;
553
+ history = route.history.slice(0, historyIndex);
382
554
  }
383
- const route = state.routes[index];
384
555
  let params;
385
556
  if (action.payload.merge) {
386
557
  params = action.payload.params !== undefined || routeParamList[route.name] !== undefined ? {
387
558
  ...routeParamList[route.name],
388
- ...route.params,
559
+ ...baseParams,
389
560
  ...action.payload.params
390
- } : route.params;
561
+ } : baseParams;
391
562
  } else {
392
563
  params = createParamsFromAction({
393
564
  action,
394
565
  routeParamList
395
566
  });
396
567
  }
397
- return {
398
- ...state,
399
- index,
400
- routes: [...state.routes.slice(0, index), params !== route.params ? {
401
- ...route,
402
- params
403
- } : state.routes[index]]
404
- };
568
+ return retainRoutes(state, getStateWithRoutes(state, [...routes.slice(0, index), params !== baseParams || historyIndex !== undefined ? {
569
+ ...route,
570
+ params,
571
+ history
572
+ } : route]));
405
573
  }
406
574
  case 'GO_BACK':
407
- if (state.index > 0 || state.routes[state.index].history?.length) {
408
- return router.getStateForAction(state, {
409
- type: 'POP',
410
- payload: {
411
- count: 1
412
- },
413
- target: action.target,
414
- source: action.source
415
- }, options);
575
+ {
576
+ const route = routes[state.index];
577
+ if (route == null) {
578
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
579
+ }
580
+ if (state.index > 0 || route.history?.length) {
581
+ return router.getStateForAction(state, {
582
+ type: 'POP',
583
+ payload: {
584
+ count: 1
585
+ },
586
+ target: action.target,
587
+ source: action.source
588
+ }, options);
589
+ }
590
+ return null;
591
+ }
592
+ case 'RETAIN':
593
+ {
594
+ const route = routes[state.index];
595
+ if (route == null) {
596
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
597
+ }
598
+ const routeKey = action.source ?? route.key;
599
+ if (!state.routes.some(route => route.key === routeKey)) {
600
+ return null;
601
+ }
602
+ if (action.payload.enable) {
603
+ if (state.retainedRouteKeys.includes(routeKey)) {
604
+ return state;
605
+ }
606
+ return {
607
+ ...state,
608
+ retainedRouteKeys: [...state.retainedRouteKeys, routeKey]
609
+ };
610
+ }
611
+ if (!state.retainedRouteKeys.includes(routeKey)) {
612
+ return state;
613
+ }
614
+ return getStateWithRoutes({
615
+ ...state,
616
+ retainedRouteKeys: state.retainedRouteKeys.filter(key => key !== routeKey)
617
+ }, routes, preloadedRoutes.filter(route => route.key !== routeKey));
416
618
  }
417
- return null;
418
619
  case 'PRELOAD':
419
620
  {
621
+ if (!state.routeNames.includes(action.payload.name)) {
622
+ return null;
623
+ }
420
624
  const getId = options.routeGetIdList[action.payload.name];
421
625
  const id = getId?.({
422
626
  params: action.payload.params
423
627
  });
424
- let route;
425
- if (id !== undefined) {
426
- route = state.routes.find(route => route.name === action.payload.name && id === getId?.({
427
- params: route.params
428
- }));
429
- }
628
+ const params = createParamsFromAction({
629
+ action,
630
+ routeParamList
631
+ });
632
+ const route = preloadedRoutes.findLast(route => route.name === action.payload.name && (id === undefined || id === getId?.({
633
+ params: route.params
634
+ })));
430
635
  if (route) {
431
- return {
432
- ...state,
433
- routes: state.routes.map(r => {
434
- if (r.key !== route?.key) {
435
- return r;
436
- }
437
- return {
438
- ...r,
439
- params: createParamsFromAction({
440
- action,
441
- routeParamList
442
- })
443
- };
444
- })
445
- };
446
- } else {
447
- return {
448
- ...state,
449
- preloadedRoutes: state.preloadedRoutes.filter(r => r.name !== action.payload.name || id !== getId?.({
450
- params: r.params
451
- })).concat(createRouteFromAction({
452
- action,
453
- routeParamList
454
- }))
455
- };
636
+ return getStateWithRoutes(state, routes, preloadedRoutes.map(r => r.key === route.key && r.params !== params ? {
637
+ ...r,
638
+ params
639
+ } : r));
456
640
  }
641
+ return getStateWithRoutes(state, routes, preloadedRoutes.concat(createRouteFromAction({
642
+ action,
643
+ routeParamList
644
+ })));
457
645
  }
458
646
  default:
459
647
  return BaseRouter.getStateForAction(state, action);
460
648
  }
461
649
  },
462
- getRoutesFromState(state) {
463
- return [...state.routes, ...state.preloadedRoutes];
464
- },
465
650
  actionCreators: StackActions
466
651
  };
467
652
  return router;