@react-navigation/core 7.0.0-rc.13 → 7.0.0-rc.14

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.
@@ -37,6 +37,11 @@ const getActiveRoute = (state: State): { name: string; params?: object } => {
37
37
  return route;
38
38
  };
39
39
 
40
+ let cachedNormalizedConfigs: [
41
+ PathConfigMap<{}> | undefined,
42
+ Record<string, ConfigItem>,
43
+ ] = [undefined, {}];
44
+
40
45
  /**
41
46
  * Utility to serialize a navigation state object to a path string.
42
47
  *
@@ -81,9 +86,13 @@ export function getPathFromState<ParamList extends {}>(
81
86
  }
82
87
 
83
88
  // Create a normalized configs object which will be easier to use
84
- const configs: Record<string, ConfigItem> = options?.screens
85
- ? createNormalizedConfigs(options?.screens)
86
- : {};
89
+ if (cachedNormalizedConfigs[0] !== options?.screens) {
90
+ cachedNormalizedConfigs = [
91
+ options?.screens,
92
+ options?.screens ? createNormalizedConfigs(options.screens) : {},
93
+ ];
94
+ }
95
+ const configs: Record<string, ConfigItem> = cachedNormalizedConfigs[1];
87
96
 
88
97
  let path = '/';
89
98
  let current: State | undefined = state;
@@ -42,6 +42,12 @@ type ParsedRoute = {
42
42
  params?: Record<string, any> | undefined;
43
43
  };
44
44
 
45
+ type ConfigResources = {
46
+ initialRoutes: InitialRouteConfig[];
47
+ configs: RouteConfig[];
48
+ configWithRegexes: RouteConfig[];
49
+ };
50
+
45
51
  /**
46
52
  * Utility to parse a path string to initial state object accepted by the container.
47
53
  * This is useful for deep linking when we need to handle the incoming URL.
@@ -67,18 +73,8 @@ export function getStateFromPath<ParamList extends {}>(
67
73
  path: string,
68
74
  options?: Options<ParamList>
69
75
  ): ResultState | undefined {
70
- if (options) {
71
- validatePathConfig(options);
72
- }
73
-
74
- const initialRoutes: InitialRouteConfig[] = [];
75
-
76
- if (options?.initialRouteName) {
77
- initialRoutes.push({
78
- initialRouteName: options.initialRouteName,
79
- parentScreens: [],
80
- });
81
- }
76
+ const { initialRoutes, configs, configWithRegexes } =
77
+ getConfigResources(options);
82
78
 
83
79
  const screens = options?.screens;
84
80
 
@@ -122,8 +118,111 @@ export function getStateFromPath<ParamList extends {}>(
122
118
  return undefined;
123
119
  }
124
120
 
121
+ if (remaining === '/') {
122
+ // We need to add special handling of empty path so navigation to empty path also works
123
+ // When handling empty path, we should only look at the root level config
124
+ const match = configs.find(
125
+ (config) =>
126
+ config.path === '' &&
127
+ config.routeNames.every(
128
+ // Make sure that none of the parent configs have a non-empty path defined
129
+ (name) => !configs.find((c) => c.screen === name)?.path
130
+ )
131
+ );
132
+
133
+ if (match) {
134
+ return createNestedStateObject(
135
+ path,
136
+ match.routeNames.map((name) => ({ name })),
137
+ initialRoutes,
138
+ configs
139
+ );
140
+ }
141
+
142
+ return undefined;
143
+ }
144
+
145
+ let result: PartialState<NavigationState> | undefined;
146
+ let current: PartialState<NavigationState> | undefined;
147
+
148
+ // We match the whole path against the regex instead of segments
149
+ // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
150
+ const { routes, remainingPath } = matchAgainstConfigs(
151
+ remaining,
152
+ configWithRegexes
153
+ );
154
+
155
+ if (routes !== undefined) {
156
+ // This will always be empty if full path matched
157
+ current = createNestedStateObject(path, routes, initialRoutes, configs);
158
+ remaining = remainingPath;
159
+ result = current;
160
+ }
161
+
162
+ if (current == null || result == null) {
163
+ return undefined;
164
+ }
165
+
166
+ return result;
167
+ }
168
+
169
+ /**
170
+ * Reference to the last used config resources. This is used to avoid recomputing the config resources when the options are the same.
171
+ */
172
+ let cachedConfigResources: [Options<{}> | undefined, ConfigResources] = [
173
+ undefined,
174
+ prepareConfigResources(),
175
+ ];
176
+
177
+ function getConfigResources<ParamList extends {}>(
178
+ options: Options<ParamList> | undefined
179
+ ) {
180
+ if (cachedConfigResources[0] !== options) {
181
+ cachedConfigResources = [options, prepareConfigResources(options)];
182
+ }
183
+
184
+ return cachedConfigResources[1];
185
+ }
186
+
187
+ function prepareConfigResources(options?: Options<{}>) {
188
+ if (options) {
189
+ validatePathConfig(options);
190
+ }
191
+
192
+ const initialRoutes = getInitialRoutes(options);
193
+
194
+ const configs = getNormalizedConfigs(initialRoutes, options?.screens);
195
+
196
+ checkForDuplicatedConfigs(configs);
197
+
198
+ const configWithRegexes = getConfigsWithRegexes(configs);
199
+
200
+ return {
201
+ initialRoutes,
202
+ configs,
203
+ configWithRegexes,
204
+ };
205
+ }
206
+
207
+ function getInitialRoutes(options?: Options<{}>) {
208
+ const initialRoutes: InitialRouteConfig[] = [];
209
+
210
+ if (options?.initialRouteName) {
211
+ initialRoutes.push({
212
+ initialRouteName: options.initialRouteName,
213
+ parentScreens: [],
214
+ });
215
+ }
216
+
217
+ return initialRoutes;
218
+ }
219
+
220
+ function getNormalizedConfigs(
221
+ initialRoutes: InitialRouteConfig[],
222
+ screens: PathConfigMap<object> = {}
223
+ ) {
125
224
  // Create a normalized configs array which will be easier to use
126
- const configs = ([] as RouteConfig[])
225
+ return ([] as RouteConfig[])
127
226
  .concat(
128
227
  ...Object.keys(screens).map((key) =>
129
228
  createNormalizedConfigs(
@@ -185,7 +284,9 @@ export function getStateFromPath<ParamList extends {}>(
185
284
  }
186
285
  return bParts.length - aParts.length;
187
286
  });
287
+ }
188
288
 
289
+ function checkForDuplicatedConfigs(configs: RouteConfig[]) {
189
290
  // Check for duplicate patterns in the config
190
291
  configs.reduce<Record<string, RouteConfig>>((acc, config) => {
191
292
  if (acc[config.pattern]) {
@@ -214,57 +315,14 @@ export function getStateFromPath<ParamList extends {}>(
214
315
  [config.pattern]: config,
215
316
  });
216
317
  }, {});
318
+ }
217
319
 
218
- if (remaining === '/') {
219
- // We need to add special handling of empty path so navigation to empty path also works
220
- // When handling empty path, we should only look at the root level config
221
- const match = configs.find(
222
- (config) =>
223
- config.path === '' &&
224
- config.routeNames.every(
225
- // Make sure that none of the parent configs have a non-empty path defined
226
- (name) => !configs.find((c) => c.screen === name)?.path
227
- )
228
- );
229
-
230
- if (match) {
231
- return createNestedStateObject(
232
- path,
233
- match.routeNames.map((name) => ({ name })),
234
- initialRoutes,
235
- configs
236
- );
237
- }
238
-
239
- return undefined;
240
- }
241
-
242
- let result: PartialState<NavigationState> | undefined;
243
- let current: PartialState<NavigationState> | undefined;
244
-
245
- // We match the whole path against the regex instead of segments
246
- // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
247
- const { routes, remainingPath } = matchAgainstConfigs(
248
- remaining,
249
- configs.map((c) => ({
250
- ...c,
251
- // Add `$` to the regex to make sure it matches till end of the path and not just beginning
252
- regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
253
- }))
254
- );
255
-
256
- if (routes !== undefined) {
257
- // This will always be empty if full path matched
258
- current = createNestedStateObject(path, routes, initialRoutes, configs);
259
- remaining = remainingPath;
260
- result = current;
261
- }
262
-
263
- if (current == null || result == null) {
264
- return undefined;
265
- }
266
-
267
- return result;
320
+ function getConfigsWithRegexes(configs: RouteConfig[]) {
321
+ return configs.map((c) => ({
322
+ ...c,
323
+ // Add `$` to the regex to make sure it matches till end of the path and not just beginning
324
+ regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
325
+ }));
268
326
  }
269
327
 
270
328
  const joinPaths = (...paths: string[]): string =>