@react-navigation/core 6.0.3 → 6.2.0
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.
- package/lib/commonjs/BaseNavigationContainer.js +13 -9
- package/lib/commonjs/BaseNavigationContainer.js.map +1 -1
- package/lib/commonjs/CurrentRenderContext.js.map +1 -1
- package/lib/commonjs/EnsureSingleNavigator.js +5 -4
- package/lib/commonjs/EnsureSingleNavigator.js.map +1 -1
- package/lib/commonjs/NavigationContainerRefContext.js.map +1 -1
- package/lib/commonjs/NavigationContext.js.map +1 -1
- package/lib/commonjs/NavigationHelpersContext.js.map +1 -1
- package/lib/commonjs/NavigationRouteContext.js.map +1 -1
- package/lib/commonjs/SceneView.js +11 -10
- package/lib/commonjs/SceneView.js.map +1 -1
- package/lib/commonjs/UnhandledActionContext.js.map +1 -1
- package/lib/commonjs/createNavigationContainerRef.js +11 -6
- package/lib/commonjs/createNavigationContainerRef.js.map +1 -1
- package/lib/commonjs/fromEntries.js +3 -1
- package/lib/commonjs/fromEntries.js.map +1 -1
- package/lib/commonjs/getActionFromState.js +2 -1
- package/lib/commonjs/getActionFromState.js.map +1 -1
- package/lib/commonjs/getPathFromState.js +13 -3
- package/lib/commonjs/getPathFromState.js.map +1 -1
- package/lib/commonjs/getStateFromPath.js +12 -2
- package/lib/commonjs/getStateFromPath.js.map +1 -1
- package/lib/commonjs/index.js +24 -24
- package/lib/commonjs/isArrayEqual.js +9 -1
- package/lib/commonjs/isArrayEqual.js.map +1 -1
- package/lib/commonjs/isRecordEqual.js +25 -0
- package/lib/commonjs/isRecordEqual.js.map +1 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useChildListeners.js.map +1 -1
- package/lib/commonjs/useCurrentRender.js +6 -5
- package/lib/commonjs/useCurrentRender.js.map +1 -1
- package/lib/commonjs/useDescriptors.js +18 -17
- package/lib/commonjs/useDescriptors.js.map +1 -1
- package/lib/commonjs/useEventEmitter.js +7 -6
- package/lib/commonjs/useEventEmitter.js.map +1 -1
- package/lib/commonjs/useFocusEvents.js +5 -4
- package/lib/commonjs/useFocusEvents.js.map +1 -1
- package/lib/commonjs/useFocusedListenersChildrenAdapter.js +5 -4
- package/lib/commonjs/useFocusedListenersChildrenAdapter.js.map +1 -1
- package/lib/commonjs/useKeyedChildListeners.js.map +1 -1
- package/lib/commonjs/useNavigationBuilder.js +52 -20
- package/lib/commonjs/useNavigationBuilder.js.map +1 -1
- package/lib/commonjs/useNavigationCache.js +26 -10
- package/lib/commonjs/useNavigationCache.js.map +1 -1
- package/lib/commonjs/useNavigationHelpers.js +32 -10
- package/lib/commonjs/useNavigationHelpers.js.map +1 -1
- package/lib/commonjs/useOnAction.js +13 -11
- package/lib/commonjs/useOnAction.js.map +1 -1
- package/lib/commonjs/useOnGetState.js +5 -4
- package/lib/commonjs/useOnGetState.js.map +1 -1
- package/lib/commonjs/useOnPreventRemove.js +6 -5
- package/lib/commonjs/useOnPreventRemove.js.map +1 -1
- package/lib/commonjs/useOnRouteFocus.js +7 -6
- package/lib/commonjs/useOnRouteFocus.js.map +1 -1
- package/lib/commonjs/useOptionsGetters.js +6 -5
- package/lib/commonjs/useOptionsGetters.js.map +1 -1
- package/lib/commonjs/useRouteCache.js +1 -1
- package/lib/commonjs/useScheduleUpdate.js +1 -1
- package/lib/commonjs/validatePathConfig.js +5 -2
- package/lib/commonjs/validatePathConfig.js.map +1 -1
- package/lib/module/BaseNavigationContainer.js +13 -9
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/CurrentRenderContext.js.map +1 -1
- package/lib/module/EnsureSingleNavigator.js +4 -3
- package/lib/module/EnsureSingleNavigator.js.map +1 -1
- package/lib/module/NavigationContainerRefContext.js.map +1 -1
- package/lib/module/NavigationContext.js.map +1 -1
- package/lib/module/NavigationHelpersContext.js.map +1 -1
- package/lib/module/NavigationRouteContext.js.map +1 -1
- package/lib/module/SceneView.js +11 -10
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/UnhandledActionContext.js.map +1 -1
- package/lib/module/createNavigationContainerRef.js +10 -5
- package/lib/module/createNavigationContainerRef.js.map +1 -1
- package/lib/module/fromEntries.js +3 -1
- package/lib/module/fromEntries.js.map +1 -1
- package/lib/module/getActionFromState.js +2 -1
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getPathFromState.js +13 -3
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getStateFromPath.js +12 -2
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/isArrayEqual.js +9 -1
- package/lib/module/isArrayEqual.js.map +1 -1
- package/lib/module/isRecordEqual.js +18 -0
- package/lib/module/isRecordEqual.js.map +1 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useChildListeners.js.map +1 -1
- package/lib/module/useCurrentRender.js +6 -5
- package/lib/module/useCurrentRender.js.map +1 -1
- package/lib/module/useDescriptors.js +18 -17
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useEventEmitter.js +7 -6
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useFocusEvents.js +5 -4
- package/lib/module/useFocusEvents.js.map +1 -1
- package/lib/module/useFocusedListenersChildrenAdapter.js +5 -4
- package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
- package/lib/module/useKeyedChildListeners.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +51 -20
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +26 -10
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +32 -10
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useOnAction.js +13 -11
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOnGetState.js +5 -4
- package/lib/module/useOnGetState.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +6 -5
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/module/useOnRouteFocus.js +7 -6
- package/lib/module/useOnRouteFocus.js.map +1 -1
- package/lib/module/useOptionsGetters.js +6 -5
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/validatePathConfig.js +5 -2
- package/lib/module/validatePathConfig.js.map +1 -1
- package/lib/typescript/src/NavigationContext.d.ts +1 -1
- package/lib/typescript/src/SceneView.d.ts +1 -1
- package/lib/typescript/src/isRecordEqual.d.ts +4 -0
- package/lib/typescript/src/types.d.ts +50 -18
- package/lib/typescript/src/useDescriptors.d.ts +13 -10
- package/lib/typescript/src/useNavigationBuilder.d.ts +12 -73
- package/lib/typescript/src/useNavigationCache.d.ts +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +7 -69
- package/package.json +5 -5
- package/src/CurrentRenderContext.tsx +3 -2
- package/src/EnsureSingleNavigator.tsx +7 -8
- package/src/NavigationContainerRefContext.tsx +3 -4
- package/src/NavigationContext.tsx +3 -2
- package/src/NavigationHelpersContext.tsx +3 -2
- package/src/NavigationRouteContext.tsx +3 -2
- package/src/SceneView.tsx +7 -1
- package/src/UnhandledActionContext.tsx +3 -4
- package/src/createNavigationContainerRef.tsx +3 -1
- package/src/isArrayEqual.tsx +9 -1
- package/src/isRecordEqual.tsx +20 -0
- package/src/types.tsx +70 -21
- package/src/useChildListeners.tsx +3 -5
- package/src/useDescriptors.tsx +16 -8
- package/src/useKeyedChildListeners.tsx +6 -8
- package/src/useNavigationBuilder.tsx +90 -27
- package/src/useNavigationCache.tsx +17 -1
- package/src/useNavigationHelpers.tsx +39 -15
|
@@ -6,9 +6,8 @@ import type { NavigationContainerRef } from './types';
|
|
|
6
6
|
/**
|
|
7
7
|
* Context which holds the route prop for a screen.
|
|
8
8
|
*/
|
|
9
|
-
const NavigationContainerRefContext =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
);
|
|
9
|
+
const NavigationContainerRefContext = React.createContext<
|
|
10
|
+
NavigationContainerRef<ParamListBase> | undefined
|
|
11
|
+
>(undefined);
|
|
13
12
|
|
|
14
13
|
export default NavigationContainerRefContext;
|
|
@@ -6,7 +6,8 @@ import type { NavigationProp } from './types';
|
|
|
6
6
|
/**
|
|
7
7
|
* Context which holds the navigation prop for a screen.
|
|
8
8
|
*/
|
|
9
|
-
const NavigationContext =
|
|
10
|
-
|
|
9
|
+
const NavigationContext = React.createContext<
|
|
10
|
+
NavigationProp<ParamListBase> | undefined
|
|
11
|
+
>(undefined);
|
|
11
12
|
|
|
12
13
|
export default NavigationContext;
|
|
@@ -7,7 +7,8 @@ import type { NavigationHelpers } from './types';
|
|
|
7
7
|
* Context which holds the navigation helpers of the parent navigator.
|
|
8
8
|
* Navigators should use this context in their view component.
|
|
9
9
|
*/
|
|
10
|
-
const NavigationHelpersContext =
|
|
11
|
-
|
|
10
|
+
const NavigationHelpersContext = React.createContext<
|
|
11
|
+
NavigationHelpers<ParamListBase> | undefined
|
|
12
|
+
>(undefined);
|
|
12
13
|
|
|
13
14
|
export default NavigationHelpersContext;
|
|
@@ -4,7 +4,8 @@ import * as React from 'react';
|
|
|
4
4
|
/**
|
|
5
5
|
* Context which holds the route prop for a screen.
|
|
6
6
|
*/
|
|
7
|
-
const NavigationRouteContext =
|
|
8
|
-
|
|
7
|
+
const NavigationRouteContext = React.createContext<Route<string> | undefined>(
|
|
8
|
+
undefined
|
|
9
|
+
);
|
|
9
10
|
|
|
10
11
|
export default NavigationRouteContext;
|
package/src/SceneView.tsx
CHANGED
|
@@ -14,7 +14,13 @@ import useOptionsGetters from './useOptionsGetters';
|
|
|
14
14
|
|
|
15
15
|
type Props<State extends NavigationState, ScreenOptions extends {}> = {
|
|
16
16
|
screen: RouteConfigComponent<ParamListBase, string> & { name: string };
|
|
17
|
-
navigation: NavigationProp<
|
|
17
|
+
navigation: NavigationProp<
|
|
18
|
+
ParamListBase,
|
|
19
|
+
string,
|
|
20
|
+
string | undefined,
|
|
21
|
+
State,
|
|
22
|
+
ScreenOptions
|
|
23
|
+
>;
|
|
18
24
|
route: Route<string>;
|
|
19
25
|
routeState: NavigationState | PartialState<NavigationState> | undefined;
|
|
20
26
|
getState: () => State;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { NavigationAction } from '@react-navigation/routers';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
-
const UnhandledActionContext =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
);
|
|
4
|
+
const UnhandledActionContext = React.createContext<
|
|
5
|
+
((action: NavigationAction) => void) | undefined
|
|
6
|
+
>(undefined);
|
|
8
7
|
|
|
9
8
|
export default UnhandledActionContext;
|
|
@@ -33,7 +33,9 @@ export default function createNavigationContainerRef<
|
|
|
33
33
|
event: string,
|
|
34
34
|
callback: (...args: any[]) => void
|
|
35
35
|
) => {
|
|
36
|
-
|
|
36
|
+
if (listeners[event]) {
|
|
37
|
+
listeners[event] = listeners[event].filter((cb) => cb !== callback);
|
|
38
|
+
}
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
let current: NavigationContainerRef<ParamList> | null = null;
|
package/src/isArrayEqual.tsx
CHANGED
|
@@ -3,5 +3,13 @@
|
|
|
3
3
|
* We need to make sure that both values and order match.
|
|
4
4
|
*/
|
|
5
5
|
export default function isArrayEqual(a: any[], b: any[]) {
|
|
6
|
-
|
|
6
|
+
if (a === b) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (a.length !== b.length) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return a.every((it, index) => it === b[index]);
|
|
7
15
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare two records with primitive values as the content.
|
|
3
|
+
*/
|
|
4
|
+
export default function isRecordEqual(
|
|
5
|
+
a: Record<string, any>,
|
|
6
|
+
b: Record<string, any>
|
|
7
|
+
) {
|
|
8
|
+
if (a === b) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const aKeys = Object.keys(a);
|
|
13
|
+
const bKeys = Object.keys(b);
|
|
14
|
+
|
|
15
|
+
if (aKeys.length !== bKeys.length) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return aKeys.every((key) => a[key] === b[key]);
|
|
20
|
+
}
|
package/src/types.tsx
CHANGED
|
@@ -25,6 +25,10 @@ export type DefaultNavigatorOptions<
|
|
|
25
25
|
ScreenOptions extends {},
|
|
26
26
|
EventMap extends EventMapBase
|
|
27
27
|
> = DefaultRouterOptions<Keyof<ParamList>> & {
|
|
28
|
+
/**
|
|
29
|
+
* Optional ID for the navigator. Can be used with `navigation.getParent(id)` to refer to a parent.
|
|
30
|
+
*/
|
|
31
|
+
id?: string;
|
|
28
32
|
/**
|
|
29
33
|
* Children React Elements to extract the route configuration from.
|
|
30
34
|
* Only `Screen`, `Group` and `React.Fragment` are supported as children.
|
|
@@ -153,7 +157,7 @@ export type EventEmitter<EventMap extends EventMapBase> = {
|
|
|
153
157
|
>;
|
|
154
158
|
};
|
|
155
159
|
|
|
156
|
-
export class PrivateValueStore<
|
|
160
|
+
export class PrivateValueStore<T extends [any, any, any]> {
|
|
157
161
|
/**
|
|
158
162
|
* UGLY HACK! DO NOT USE THE TYPE!!!
|
|
159
163
|
*
|
|
@@ -164,7 +168,7 @@ export class PrivateValueStore<A, B, C> {
|
|
|
164
168
|
* Adding private keyword works, but the annotation is stripped away in declaration.
|
|
165
169
|
* Turns out if we use an empty string, it doesn't show up in intelliSense.
|
|
166
170
|
*/
|
|
167
|
-
protected ''?:
|
|
171
|
+
protected ''?: T;
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
type NavigationHelpersCommon<
|
|
@@ -236,16 +240,26 @@ type NavigationHelpersCommon<
|
|
|
236
240
|
canGoBack(): boolean;
|
|
237
241
|
|
|
238
242
|
/**
|
|
239
|
-
* Returns the
|
|
243
|
+
* Returns the name of the navigator specified in the `name` prop.
|
|
244
|
+
* If no name is specified, returns `undefined`.
|
|
245
|
+
*/
|
|
246
|
+
getId(): string | undefined;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Returns the navigation helpers from a parent navigator based on the ID.
|
|
250
|
+
* If an ID is provided, the navigation helper from the parent navigator with matching ID (including current) will be returned.
|
|
251
|
+
* If no ID is provided, the navigation helper from the immediate parent navigator will be returned.
|
|
252
|
+
*
|
|
253
|
+
* @param id Optional ID of a parent navigator.
|
|
240
254
|
*/
|
|
241
|
-
getParent<T =
|
|
255
|
+
getParent<T = NavigationHelpers<ParamListBase> | undefined>(id?: string): T;
|
|
242
256
|
|
|
243
257
|
/**
|
|
244
258
|
* Returns the navigator's state.
|
|
245
259
|
* Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
|
|
246
260
|
*/
|
|
247
261
|
getState(): State;
|
|
248
|
-
} & PrivateValueStore<ParamList,
|
|
262
|
+
} & PrivateValueStore<[ParamList, unknown, unknown]>;
|
|
249
263
|
|
|
250
264
|
export type NavigationHelpers<
|
|
251
265
|
ParamList extends ParamListBase,
|
|
@@ -291,10 +305,20 @@ export type NavigationContainerProps = {
|
|
|
291
305
|
export type NavigationProp<
|
|
292
306
|
ParamList extends {},
|
|
293
307
|
RouteName extends keyof ParamList = Keyof<ParamList>,
|
|
308
|
+
NavigatorID extends string | undefined = undefined,
|
|
294
309
|
State extends NavigationState = NavigationState<ParamList>,
|
|
295
310
|
ScreenOptions extends {} = {},
|
|
296
311
|
EventMap extends EventMapBase = {}
|
|
297
|
-
> = NavigationHelpersCommon<ParamList, State> & {
|
|
312
|
+
> = Omit<NavigationHelpersCommon<ParamList, State>, 'getParent'> & {
|
|
313
|
+
/**
|
|
314
|
+
* Returns the navigation prop from a parent navigator based on the ID.
|
|
315
|
+
* If an ID is provided, the navigation prop from the parent navigator with matching ID (including current) will be returned.
|
|
316
|
+
* If no ID is provided, the navigation prop from the immediate parent navigator will be returned.
|
|
317
|
+
*
|
|
318
|
+
* @param id Optional ID of a parent navigator.
|
|
319
|
+
*/
|
|
320
|
+
getParent<T = NavigationProp<ParamListBase> | undefined>(id?: NavigatorID): T;
|
|
321
|
+
|
|
298
322
|
/**
|
|
299
323
|
* Update the param object for the route.
|
|
300
324
|
* The new params will be shallow merged with the old one.
|
|
@@ -311,7 +335,7 @@ export type NavigationProp<
|
|
|
311
335
|
*/
|
|
312
336
|
setOptions(options: Partial<ScreenOptions>): void;
|
|
313
337
|
} & EventConsumer<EventMap & EventMapCore<State>> &
|
|
314
|
-
PrivateValueStore<ParamList, RouteName, EventMap>;
|
|
338
|
+
PrivateValueStore<[ParamList, RouteName, EventMap]>;
|
|
315
339
|
|
|
316
340
|
export type RouteProp<
|
|
317
341
|
ParamList extends ParamListBase,
|
|
@@ -319,7 +343,7 @@ export type RouteProp<
|
|
|
319
343
|
> = Route<Extract<RouteName, string>, ParamList[RouteName]>;
|
|
320
344
|
|
|
321
345
|
export type CompositeNavigationProp<
|
|
322
|
-
A extends NavigationProp<ParamListBase, string, any, any>,
|
|
346
|
+
A extends NavigationProp<ParamListBase, string, any, any, any>,
|
|
323
347
|
B extends NavigationHelpersCommon<ParamListBase, any>
|
|
324
348
|
> = Omit<A & B, keyof NavigationProp<any>> &
|
|
325
349
|
NavigationProp<
|
|
@@ -334,30 +358,42 @@ export type CompositeNavigationProp<
|
|
|
334
358
|
* Ideally it should work for any of them, but it's not possible to infer that way
|
|
335
359
|
*/
|
|
336
360
|
A extends NavigationProp<any, infer R> ? R : string,
|
|
361
|
+
/**
|
|
362
|
+
* ID from both navigation objects needs to be combined for `getParent`
|
|
363
|
+
*/
|
|
364
|
+
| (A extends NavigationProp<any, any, infer I> ? I : never)
|
|
365
|
+
| (B extends NavigationProp<any, any, infer J> ? J : never),
|
|
337
366
|
/**
|
|
338
367
|
* The type of state should refer to the state specified in the first type
|
|
339
368
|
*/
|
|
340
|
-
A extends NavigationProp<any, any, infer S> ? S : NavigationState,
|
|
369
|
+
A extends NavigationProp<any, any, any, infer S> ? S : NavigationState,
|
|
341
370
|
/**
|
|
342
371
|
* Screen options from both navigation objects needs to be combined
|
|
343
372
|
* This allows typechecking `setOptions`
|
|
344
373
|
*/
|
|
345
|
-
(A extends NavigationProp<any, any, any, infer O> ? O : {}) &
|
|
346
|
-
(B extends NavigationProp<any, any, any, infer P> ? P : {}),
|
|
374
|
+
(A extends NavigationProp<any, any, any, any, infer O> ? O : {}) &
|
|
375
|
+
(B extends NavigationProp<any, any, any, any, infer P> ? P : {}),
|
|
347
376
|
/**
|
|
348
377
|
* Event consumer config should refer to the config specified in the first type
|
|
349
378
|
* This allows typechecking `addListener`/`removeListener`
|
|
350
379
|
*/
|
|
351
|
-
A extends NavigationProp<any, any, any, any, infer E> ? E : {}
|
|
380
|
+
A extends NavigationProp<any, any, any, any, any, infer E> ? E : {}
|
|
352
381
|
>;
|
|
353
382
|
|
|
354
383
|
export type CompositeScreenProps<
|
|
355
384
|
A extends {
|
|
356
|
-
navigation: NavigationProp<
|
|
385
|
+
navigation: NavigationProp<
|
|
386
|
+
ParamListBase,
|
|
387
|
+
string,
|
|
388
|
+
string | undefined,
|
|
389
|
+
any,
|
|
390
|
+
any,
|
|
391
|
+
any
|
|
392
|
+
>;
|
|
357
393
|
route: RouteProp<ParamListBase>;
|
|
358
394
|
},
|
|
359
395
|
B extends {
|
|
360
|
-
navigation: NavigationHelpersCommon<
|
|
396
|
+
navigation: NavigationHelpersCommon<any, any>;
|
|
361
397
|
}
|
|
362
398
|
> = {
|
|
363
399
|
navigation: CompositeNavigationProp<A['navigation'], B['navigation']>;
|
|
@@ -366,7 +402,7 @@ export type CompositeScreenProps<
|
|
|
366
402
|
|
|
367
403
|
export type Descriptor<
|
|
368
404
|
ScreenOptions extends {},
|
|
369
|
-
Navigation extends NavigationProp<any, any, any, any, any>,
|
|
405
|
+
Navigation extends NavigationProp<any, any, any, any, any, any>,
|
|
370
406
|
Route extends RouteProp<any, any>
|
|
371
407
|
> = {
|
|
372
408
|
/**
|
|
@@ -393,12 +429,12 @@ export type Descriptor<
|
|
|
393
429
|
export type ScreenListeners<
|
|
394
430
|
State extends NavigationState,
|
|
395
431
|
EventMap extends EventMapBase
|
|
396
|
-
> = Partial<
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
>;
|
|
432
|
+
> = Partial<{
|
|
433
|
+
[EventName in keyof (EventMap & EventMapCore<State>)]: EventListenerCallback<
|
|
434
|
+
EventMap,
|
|
435
|
+
EventName
|
|
436
|
+
>;
|
|
437
|
+
}>;
|
|
402
438
|
|
|
403
439
|
export type RouteConfigComponent<
|
|
404
440
|
ParamList extends ParamListBase,
|
|
@@ -439,6 +475,13 @@ export type RouteConfig<
|
|
|
439
475
|
ScreenOptions extends {},
|
|
440
476
|
EventMap extends EventMapBase
|
|
441
477
|
> = {
|
|
478
|
+
/**
|
|
479
|
+
* Optional key for this screen. This doesn't need to be unique.
|
|
480
|
+
* If the key changes, existing screens with this name will be removed or reset.
|
|
481
|
+
* Useful when we have some common screens and have conditional rendering.
|
|
482
|
+
*/
|
|
483
|
+
navigationKey?: string;
|
|
484
|
+
|
|
442
485
|
/**
|
|
443
486
|
* Route name of this screen.
|
|
444
487
|
*/
|
|
@@ -482,6 +525,12 @@ export type RouteGroupConfig<
|
|
|
482
525
|
ParamList extends ParamListBase,
|
|
483
526
|
ScreenOptions extends {}
|
|
484
527
|
> = {
|
|
528
|
+
/**
|
|
529
|
+
* Optional key for the screens in this group.
|
|
530
|
+
* If the key changes, all existing screens in this group will be removed or reset.
|
|
531
|
+
*/
|
|
532
|
+
navigationKey?: string;
|
|
533
|
+
|
|
485
534
|
/**
|
|
486
535
|
* Navigator options for this screen.
|
|
487
536
|
*/
|
|
@@ -6,11 +6,9 @@ import type { ListenerMap } from './NavigationBuilderContext';
|
|
|
6
6
|
* Hook which lets child navigators add action listeners.
|
|
7
7
|
*/
|
|
8
8
|
export default function useChildListeners() {
|
|
9
|
-
const { current: listeners } = React.useRef<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
>({
|
|
9
|
+
const { current: listeners } = React.useRef<{
|
|
10
|
+
[K in keyof ListenerMap]: ListenerMap[K][];
|
|
11
|
+
}>({
|
|
14
12
|
action: [],
|
|
15
13
|
focus: [],
|
|
16
14
|
});
|
package/src/useDescriptors.tsx
CHANGED
|
@@ -29,10 +29,11 @@ export type ScreenConfigWithParent<
|
|
|
29
29
|
State extends NavigationState,
|
|
30
30
|
ScreenOptions extends {},
|
|
31
31
|
EventMap extends EventMapBase
|
|
32
|
-
> =
|
|
33
|
-
(
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
> = {
|
|
33
|
+
keys: (string | undefined)[];
|
|
34
|
+
options: (ScreenOptionsOrCallback<ScreenOptions> | undefined)[] | undefined;
|
|
35
|
+
props: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
|
|
36
|
+
};
|
|
36
37
|
|
|
37
38
|
type ScreenOptionsOrCallback<ScreenOptions extends {}> =
|
|
38
39
|
| ScreenOptions
|
|
@@ -142,22 +143,29 @@ export default function useDescriptors<
|
|
|
142
143
|
string,
|
|
143
144
|
Descriptor<
|
|
144
145
|
ScreenOptions,
|
|
145
|
-
NavigationProp<
|
|
146
|
+
NavigationProp<
|
|
147
|
+
ParamListBase,
|
|
148
|
+
string,
|
|
149
|
+
string | undefined,
|
|
150
|
+
State,
|
|
151
|
+
ScreenOptions,
|
|
152
|
+
EventMap
|
|
153
|
+
> &
|
|
146
154
|
ActionHelpers,
|
|
147
155
|
RouteProp<ParamListBase>
|
|
148
156
|
>
|
|
149
157
|
>
|
|
150
158
|
>((acc, route, i) => {
|
|
151
159
|
const config = screens[route.name];
|
|
152
|
-
const screen = config
|
|
160
|
+
const screen = config.props;
|
|
153
161
|
const navigation = navigations[route.key];
|
|
154
162
|
|
|
155
163
|
const optionsList = [
|
|
156
164
|
// The default `screenOptions` passed to the navigator
|
|
157
165
|
screenOptions,
|
|
158
166
|
// The `screenOptions` props passed to `Group` elements
|
|
159
|
-
...((config
|
|
160
|
-
? config
|
|
167
|
+
...((config.options
|
|
168
|
+
? config.options.filter(Boolean)
|
|
161
169
|
: []) as ScreenOptionsOrCallback<ScreenOptions>[]),
|
|
162
170
|
// The `options` prop passed to `Screen` elements,
|
|
163
171
|
screen.options,
|
|
@@ -6,14 +6,12 @@ import type { KeyedListenerMap } from './NavigationBuilderContext';
|
|
|
6
6
|
* Hook which lets child navigators add getters to be called for obtaining rehydrated state.
|
|
7
7
|
*/
|
|
8
8
|
export default function useKeyedChildListeners() {
|
|
9
|
-
const { current: keyedListeners } = React.useRef<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
>({
|
|
9
|
+
const { current: keyedListeners } = React.useRef<{
|
|
10
|
+
[K in keyof KeyedListenerMap]: Record<
|
|
11
|
+
string,
|
|
12
|
+
KeyedListenerMap[K] | undefined
|
|
13
|
+
>;
|
|
14
|
+
}>({
|
|
17
15
|
getState: {},
|
|
18
16
|
beforeRemove: {},
|
|
19
17
|
});
|
|
@@ -15,6 +15,7 @@ import { isValidElementType } from 'react-is';
|
|
|
15
15
|
|
|
16
16
|
import Group from './Group';
|
|
17
17
|
import isArrayEqual from './isArrayEqual';
|
|
18
|
+
import isRecordEqual from './isRecordEqual';
|
|
18
19
|
import NavigationHelpersContext from './NavigationHelpersContext';
|
|
19
20
|
import NavigationRouteContext from './NavigationRouteContext';
|
|
20
21
|
import NavigationStateContext from './NavigationStateContext';
|
|
@@ -51,6 +52,9 @@ type NavigatorRoute<State extends NavigationState> = {
|
|
|
51
52
|
params?: NavigatorScreenParams<ParamListBase, State>;
|
|
52
53
|
};
|
|
53
54
|
|
|
55
|
+
const isValidKey = (key: unknown) =>
|
|
56
|
+
key === undefined || (typeof key === 'string' && key !== '');
|
|
57
|
+
|
|
54
58
|
/**
|
|
55
59
|
* Extract route config object from React children elements.
|
|
56
60
|
*
|
|
@@ -62,7 +66,12 @@ const getRouteConfigsFromChildren = <
|
|
|
62
66
|
EventMap extends EventMapBase
|
|
63
67
|
>(
|
|
64
68
|
children: React.ReactNode,
|
|
65
|
-
|
|
69
|
+
groupKey?: string,
|
|
70
|
+
groupOptions?: ScreenConfigWithParent<
|
|
71
|
+
State,
|
|
72
|
+
ScreenOptions,
|
|
73
|
+
EventMap
|
|
74
|
+
>['options']
|
|
66
75
|
) => {
|
|
67
76
|
const configs = React.Children.toArray(children).reduce<
|
|
68
77
|
ScreenConfigWithParent<State, ScreenOptions, EventMap>[]
|
|
@@ -71,29 +80,50 @@ const getRouteConfigsFromChildren = <
|
|
|
71
80
|
if (child.type === Screen) {
|
|
72
81
|
// We can only extract the config from `Screen` elements
|
|
73
82
|
// If something else was rendered, it's probably a bug
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
|
|
84
|
+
if (!isValidKey(child.props.navigationKey)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Got an invalid 'navigationKey' prop (${JSON.stringify(
|
|
87
|
+
child.props.navigationKey
|
|
88
|
+
)}) for the screen '${
|
|
89
|
+
child.props.name
|
|
90
|
+
}'. It must be a non-empty string or 'undefined'.`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
acc.push({
|
|
95
|
+
keys: [groupKey, child.props.navigationKey],
|
|
96
|
+
options: groupOptions,
|
|
97
|
+
props: child.props as RouteConfig<
|
|
77
98
|
ParamListBase,
|
|
78
99
|
string,
|
|
79
100
|
State,
|
|
80
101
|
ScreenOptions,
|
|
81
102
|
EventMap
|
|
82
103
|
>,
|
|
83
|
-
|
|
104
|
+
});
|
|
84
105
|
return acc;
|
|
85
106
|
}
|
|
86
107
|
|
|
87
108
|
if (child.type === React.Fragment || child.type === Group) {
|
|
109
|
+
if (!isValidKey(child.props.navigationKey)) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Got an invalid 'navigationKey' prop (${JSON.stringify(
|
|
112
|
+
child.props.navigationKey
|
|
113
|
+
)}) for the group. It must be a non-empty string or 'undefined'.`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
88
117
|
// When we encounter a fragment or group, we need to dive into its children to extract the configs
|
|
89
118
|
// This is handy to conditionally define a group of screens
|
|
90
119
|
acc.push(
|
|
91
120
|
...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
|
|
92
121
|
child.props.children,
|
|
122
|
+
child.props.navigationKey,
|
|
93
123
|
child.type !== Group
|
|
94
|
-
?
|
|
95
|
-
:
|
|
96
|
-
? [...
|
|
124
|
+
? groupOptions
|
|
125
|
+
: groupOptions != null
|
|
126
|
+
? [...groupOptions, child.props.screenOptions]
|
|
97
127
|
: [child.props.screenOptions]
|
|
98
128
|
)
|
|
99
129
|
);
|
|
@@ -118,7 +148,7 @@ const getRouteConfigsFromChildren = <
|
|
|
118
148
|
|
|
119
149
|
if (process.env.NODE_ENV !== 'production') {
|
|
120
150
|
configs.forEach((config) => {
|
|
121
|
-
const { name, children, component, getComponent } = config
|
|
151
|
+
const { name, children, component, getComponent } = config.props;
|
|
122
152
|
|
|
123
153
|
if (typeof name !== 'string' || !name) {
|
|
124
154
|
throw new Error(
|
|
@@ -169,13 +199,19 @@ const getRouteConfigsFromChildren = <
|
|
|
169
199
|
);
|
|
170
200
|
}
|
|
171
201
|
|
|
172
|
-
if (typeof component === 'function'
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
202
|
+
if (typeof component === 'function') {
|
|
203
|
+
if (component.name === 'component') {
|
|
204
|
+
// Inline anonymous functions passed in the `component` prop will have the name of the prop
|
|
205
|
+
// It's relatively safe to assume that it's not a component since it should also have PascalCase name
|
|
206
|
+
// We won't catch all scenarios here, but this should catch a good chunk of incorrect use.
|
|
207
|
+
console.warn(
|
|
208
|
+
`Looks like you're passing an inline function for 'component' prop for the screen '${name}' (e.g. component={() => <SomeComponent />}). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.`
|
|
209
|
+
);
|
|
210
|
+
} else if (/^[a-z]/.test(component.name)) {
|
|
211
|
+
console.warn(
|
|
212
|
+
`Got a component with the name '${component.name}' for the screen '${name}'. React Components must start with an uppercase letter. If you're passing a regular function and not a component, pass it as children to 'Screen' instead. Otherwise capitalize your component's name.`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
179
215
|
}
|
|
180
216
|
} else {
|
|
181
217
|
throw new Error(
|
|
@@ -230,26 +266,36 @@ export default function useNavigationBuilder<
|
|
|
230
266
|
})
|
|
231
267
|
);
|
|
232
268
|
|
|
233
|
-
const routeConfigs =
|
|
234
|
-
|
|
269
|
+
const routeConfigs = getRouteConfigsFromChildren<
|
|
270
|
+
State,
|
|
271
|
+
ScreenOptions,
|
|
272
|
+
EventMap
|
|
273
|
+
>(children);
|
|
235
274
|
|
|
236
275
|
const screens = routeConfigs.reduce<
|
|
237
276
|
Record<string, ScreenConfigWithParent<State, ScreenOptions, EventMap>>
|
|
238
277
|
>((acc, config) => {
|
|
239
|
-
if (config
|
|
278
|
+
if (config.props.name in acc) {
|
|
240
279
|
throw new Error(
|
|
241
|
-
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config
|
|
280
|
+
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`
|
|
242
281
|
);
|
|
243
282
|
}
|
|
244
283
|
|
|
245
|
-
acc[config
|
|
284
|
+
acc[config.props.name] = config;
|
|
246
285
|
return acc;
|
|
247
286
|
}, {});
|
|
248
287
|
|
|
249
|
-
const routeNames = routeConfigs.map((config) => config
|
|
288
|
+
const routeNames = routeConfigs.map((config) => config.props.name);
|
|
289
|
+
const routeKeyList = routeNames.reduce<Record<string, React.Key | undefined>>(
|
|
290
|
+
(acc, curr) => {
|
|
291
|
+
acc[curr] = screens[curr].keys.map((key) => key ?? '').join(':');
|
|
292
|
+
return acc;
|
|
293
|
+
},
|
|
294
|
+
{}
|
|
295
|
+
);
|
|
250
296
|
const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
|
|
251
297
|
(acc, curr) => {
|
|
252
|
-
const { initialParams } = screens[curr]
|
|
298
|
+
const { initialParams } = screens[curr].props;
|
|
253
299
|
acc[curr] = initialParams;
|
|
254
300
|
return acc;
|
|
255
301
|
},
|
|
@@ -260,7 +306,7 @@ export default function useNavigationBuilder<
|
|
|
260
306
|
>(
|
|
261
307
|
(acc, curr) =>
|
|
262
308
|
Object.assign(acc, {
|
|
263
|
-
[curr]: screens[curr]
|
|
309
|
+
[curr]: screens[curr].props.getId,
|
|
264
310
|
}),
|
|
265
311
|
{}
|
|
266
312
|
);
|
|
@@ -315,7 +361,7 @@ export default function useNavigationBuilder<
|
|
|
315
361
|
const initialRouteParamList = routeNames.reduce<
|
|
316
362
|
Record<string, object | undefined>
|
|
317
363
|
>((acc, curr) => {
|
|
318
|
-
const { initialParams } = screens[curr]
|
|
364
|
+
const { initialParams } = screens[curr].props;
|
|
319
365
|
const initialParamsFromParams =
|
|
320
366
|
route?.params?.state == null &&
|
|
321
367
|
route?.params?.initial !== false &&
|
|
@@ -371,6 +417,14 @@ export default function useNavigationBuilder<
|
|
|
371
417
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
372
418
|
}, [currentState, router, isStateValid]);
|
|
373
419
|
|
|
420
|
+
const previousRouteKeyListRef = React.useRef(routeKeyList);
|
|
421
|
+
|
|
422
|
+
React.useEffect(() => {
|
|
423
|
+
previousRouteKeyListRef.current = routeKeyList;
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const previousRouteKeyList = previousRouteKeyListRef.current;
|
|
427
|
+
|
|
374
428
|
let state =
|
|
375
429
|
// If the state isn't initialized, or stale, use the state we initialized instead
|
|
376
430
|
// The state won't update until there's a change needed in the state we have initalized locally
|
|
@@ -381,12 +435,20 @@ export default function useNavigationBuilder<
|
|
|
381
435
|
|
|
382
436
|
let nextState: State = state;
|
|
383
437
|
|
|
384
|
-
if (
|
|
438
|
+
if (
|
|
439
|
+
!isArrayEqual(state.routeNames, routeNames) ||
|
|
440
|
+
!isRecordEqual(routeKeyList, previousRouteKeyList)
|
|
441
|
+
) {
|
|
385
442
|
// When the list of route names change, the router should handle it to remove invalid routes
|
|
386
443
|
nextState = router.getStateForRouteNamesChange(state, {
|
|
387
444
|
routeNames,
|
|
388
445
|
routeParamList,
|
|
389
446
|
routeGetIdList,
|
|
447
|
+
routeKeyChanges: Object.keys(routeKeyList).filter(
|
|
448
|
+
(name) =>
|
|
449
|
+
previousRouteKeyList.hasOwnProperty(name) &&
|
|
450
|
+
routeKeyList[name] !== previousRouteKeyList[name]
|
|
451
|
+
),
|
|
390
452
|
});
|
|
391
453
|
}
|
|
392
454
|
|
|
@@ -522,7 +584,7 @@ export default function useNavigationBuilder<
|
|
|
522
584
|
...[
|
|
523
585
|
screenListeners,
|
|
524
586
|
...routeNames.map((name) => {
|
|
525
|
-
const { listeners } = screens[name]
|
|
587
|
+
const { listeners } = screens[name].props;
|
|
526
588
|
return listeners;
|
|
527
589
|
}),
|
|
528
590
|
].map((listeners) => {
|
|
@@ -583,6 +645,7 @@ export default function useNavigationBuilder<
|
|
|
583
645
|
NavigationAction,
|
|
584
646
|
EventMap
|
|
585
647
|
>({
|
|
648
|
+
id: options.id,
|
|
586
649
|
onAction,
|
|
587
650
|
getState,
|
|
588
651
|
emitter,
|