feat: add a way to specify an unique ID for screens

With this, the user will be able to specify a `getId` function for their screens which returns an unique ID to use for the screen:

```js
<Stack.Screen
  name="Profile"
  component={ProfileScreen}
  getId={({ params }) => params.userId}
/>
```

This is an alternative to the `key` option in `navigate` with several advantages:

- Often users specify a key that's dependent on data already in params, such as `userId`. So it's much easier to specify it one place rather than at every call site.
- Users won't need to deal with generating a unique key for routes manually.
- This will work with other actions such as `push`, and not just navigate.
- With this, it'll be possible to have multiple instances of the screen even if you use `navigate`, which may be desirable in many cases (such as profile screens).
This commit is contained in:
Satyajit Sahoo
2021-01-14 03:33:44 +01:00
parent 802db004ae
commit 15b8bb3458
9 changed files with 385 additions and 167 deletions

View File

@@ -393,6 +393,14 @@ export type RouteConfig<
navigation: any;
}) => ScreenListeners<State, EventMap>);
/**
* Function to return an unique ID for this screen.
* Receives an object with the route params.
* For a given screen name, there will always be only one screen corresponding to an ID.
* If `undefined` is returned, it acts same as no `getId` being specified.
*/
getId?: ({ params }: { params: ParamList[RouteName] }) => string | undefined;
/**
* Initial params object for the route.
*/

View File

@@ -7,6 +7,7 @@ import {
ParamListBase,
Router,
RouterFactory,
RouterConfigOptions,
PartialState,
NavigationAction,
Route,
@@ -257,6 +258,15 @@ export default function useNavigationBuilder<
},
{}
);
const routeGetIdList = routeNames.reduce<
RouterConfigOptions['routeGetIdList']
>(
(acc, curr) =>
Object.assign(acc, {
[curr]: screens[curr].getId,
}),
{}
);
if (!routeNames.length) {
throw new Error(
@@ -297,6 +307,7 @@ export default function useNavigationBuilder<
router.getInitialState({
routeNames,
routeParamList,
routeGetIdList,
}),
true,
];
@@ -307,6 +318,7 @@ export default function useNavigationBuilder<
{
routeNames,
routeParamList,
routeGetIdList,
}
),
false,
@@ -336,6 +348,7 @@ export default function useNavigationBuilder<
nextState = router.getStateForRouteNamesChange(state, {
routeNames,
routeParamList,
routeGetIdList,
});
}
@@ -371,6 +384,7 @@ export default function useNavigationBuilder<
? router.getStateForAction(nextState, action, {
routeNames,
routeParamList,
routeGetIdList,
})
: null;
@@ -379,6 +393,7 @@ export default function useNavigationBuilder<
? router.getRehydratedState(updatedState, {
routeNames,
routeParamList,
routeGetIdList,
})
: nextState;
}
@@ -500,6 +515,7 @@ export default function useNavigationBuilder<
routerConfigOptions: {
routeNames,
routeParamList,
routeGetIdList,
},
emitter,
});

View File

@@ -78,6 +78,7 @@ export default function useNavigationHelpers<
router.getStateForAction(state, CommonActions.goBack() as Action, {
routeNames: state.routeNames,
routeParamList: {},
routeGetIdList: {},
}) !== null ||
parentNavigationHelpers?.canGoBack() ||
false

View File

@@ -120,8 +120,12 @@ export default function DrawerRouter({
type: 'drawer',
getInitialState({ routeNames, routeParamList }) {
let state = router.getInitialState({ routeNames, routeParamList });
getInitialState({ routeNames, routeParamList, routeGetIdList }) {
let state = router.getInitialState({
routeNames,
routeParamList,
routeGetIdList,
});
if (openByDefault) {
state = openDrawer(state);
@@ -135,7 +139,10 @@ export default function DrawerRouter({
};
},
getRehydratedState(partialState, { routeNames, routeParamList }) {
getRehydratedState(
partialState,
{ routeNames, routeParamList, routeGetIdList }
) {
if (partialState.stale === false) {
return partialState;
}
@@ -143,6 +150,7 @@ export default function DrawerRouter({
let state = router.getRehydratedState(partialState, {
routeNames,
routeParamList,
routeGetIdList,
});
if (isDrawerOpen(partialState)) {

View File

@@ -18,7 +18,7 @@ export type StackActionType =
}
| {
type: 'PUSH';
payload: { name: string; key?: string | undefined; params?: object };
payload: { name: string; params?: object };
source?: string;
target?: string;
}
@@ -258,41 +258,34 @@ export default function StackRouter(options: StackRouterOptions) {
case 'PUSH':
if (state.routeNames.includes(action.payload.name)) {
const route =
action.payload.name && action.payload.key
? state.routes.find(
(route) =>
route.name === action.payload.name &&
route.key === action.payload.key
)
: undefined;
const getId = options.routeGetIdList[action.payload.name];
const id = getId?.({ params: action.payload.params });
const route = id
? state.routes.find(
(route) => id === getId?.({ params: route.params })
)
: undefined;
let routes: Route<string>[];
if (route) {
routes = state.routes.filter((r) => r.key !== route.key);
routes.push(
action.payload.params
? {
...route,
params:
action.payload.params !== undefined
? {
...route.params,
...action.payload.params,
}
: route.params,
}
: route
);
routes.push({
...route,
params:
routeParamList[action.payload.name] !== undefined
? {
...routeParamList[action.payload.name],
...action.payload.params,
}
: action.payload.params,
});
} else {
routes = [
...state.routes,
{
key:
action.payload.key === undefined
? `${action.payload.name}-${nanoid()}`
: action.payload.key,
key: `${action.payload.name}-${nanoid()}`,
name: action.payload.name,
params:
routeParamList[action.payload.name] !== undefined
@@ -355,7 +348,19 @@ export default function StackRouter(options: StackRouterOptions) {
// If the route already exists, navigate to that
let index = -1;
if (
const getId =
// `getId` and `key` can't be used together
action.payload.key === undefined &&
action.payload.name !== undefined
? options.routeGetIdList[action.payload.name]
: undefined;
const id = getId?.({ params: action.payload.params });
if (id) {
index = state.routes.findIndex(
(route) => id === getId?.({ params: route.params })
);
} else if (
(state.routes[state.index].name === action.payload.name &&
action.payload.key === undefined) ||
state.routes[state.index].key === action.payload.key
@@ -383,18 +388,27 @@ export default function StackRouter(options: StackRouterOptions) {
}
if (index === -1 && action.payload.name !== undefined) {
return router.getStateForAction(
state,
const routes = [
...state.routes,
{
type: 'PUSH',
payload: {
key: action.payload.key,
name: action.payload.name,
params: action.payload.params,
},
key:
action.payload.key ?? `${action.payload.name}-${nanoid()}`,
name: action.payload.name,
params:
routeParamList[action.payload.name] !== undefined
? {
...routeParamList[action.payload.name],
...action.payload.params,
}
: action.payload.params,
},
options
);
];
return {
...state,
routes,
index: routes.length - 1,
};
}
const route = state.routes[index];

View File

@@ -4,6 +4,7 @@ import {
DrawerActions,
DrawerNavigationState,
ParamListBase,
RouterConfigOptions,
} from '..';
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
@@ -18,6 +19,7 @@ it('gets initial state from route names and params with initialRouteName', () =>
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
})
).toEqual({
index: 1,
@@ -47,6 +49,7 @@ it('gets initial state from route names and params without initialRouteName', ()
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
})
).toEqual({
index: 0,
@@ -66,12 +69,13 @@ it('gets initial state from route names and params without initialRouteName', ()
it('gets rehydrated state from partial state', () => {
const router = DrawerRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
};
expect(
@@ -223,15 +227,17 @@ it("doesn't rehydrate state if it's not stale", () => {
router.getRehydratedState(state, {
routeNames: [],
routeParamList: {},
routeGetIdList: {},
})
).toBe(state);
});
it('handles navigate action', () => {
const router = DrawerRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -267,9 +273,10 @@ it('handles navigate action', () => {
it('handles navigate action with open drawer', () => {
const router = DrawerRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -305,9 +312,10 @@ it('handles navigate action with open drawer', () => {
it('handles open drawer action', () => {
const router = DrawerRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -360,9 +368,10 @@ it('handles open drawer action', () => {
it('handles close drawer action', () => {
const router = DrawerRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -418,9 +427,10 @@ it('handles close drawer action', () => {
it('handles toggle drawer action', () => {
const router = DrawerRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar'],
routeParamList: {},
routeGetIdList: {},
};
expect(

View File

@@ -1,4 +1,9 @@
import { CommonActions, StackRouter, StackActions } from '..';
import {
CommonActions,
StackRouter,
StackActions,
RouterConfigOptions,
} from '..';
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
@@ -12,6 +17,7 @@ it('gets initial state from route names and params with initialRouteName', () =>
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
})
).toEqual({
index: 0,
@@ -33,6 +39,7 @@ it('gets initial state from route names and params without initialRouteName', ()
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
})
).toEqual({
index: 0,
@@ -47,12 +54,13 @@ it('gets initial state from route names and params without initialRouteName', ()
it('gets rehydrated state from partial state', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
};
expect(
@@ -136,6 +144,7 @@ it("doesn't rehydrate state if it's not stale", () => {
router.getRehydratedState(state, {
routeNames: [],
routeParamList: {},
routeGetIdList: {},
})
).toBe(state);
});
@@ -163,6 +172,7 @@ it('gets state on route names change', () => {
qux: { name: 'John' },
fiz: { fruit: 'apple' },
},
routeGetIdList: {},
}
)
).toEqual({
@@ -195,6 +205,7 @@ it('gets state on route names change', () => {
routeParamList: {
baz: { name: 'John' },
},
routeGetIdList: {},
}
)
).toEqual({
@@ -228,6 +239,7 @@ it('gets state on route names change with initialRouteName', () => {
routeParamList: {
baz: { name: 'John' },
},
routeGetIdList: {},
}
)
).toEqual({
@@ -242,9 +254,10 @@ it('gets state on route names change with initialRouteName', () => {
it('handles navigate action', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -427,11 +440,139 @@ it('handles navigate action', () => {
});
});
it('handles go back action', () => {
it('ensures unique ID for navigate', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {
bar: ({ params }) => params?.foo,
qux: ({ params }) => params?.fux,
},
};
expect(
router.getStateForAction(
{
stale: false,
type: 'stack',
key: 'root',
index: 0,
routeNames: ['baz', 'bar', 'qux'],
routes: [{ key: 'bar', name: 'bar' }],
},
CommonActions.navigate('bar', { foo: 'a' }),
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
});
expect(
router.getStateForAction(
{
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
},
CommonActions.navigate('bar', { foo: 'a' }),
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
});
expect(
router.getStateForAction(
{
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
},
CommonActions.navigate('bar', { foo: 'b' }),
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 2,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
{ key: 'bar-test', name: 'bar', params: { foo: 'b' } },
],
});
expect(
router.getStateForAction(
{
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
},
CommonActions.navigate({
key: 'test',
name: 'bar',
params: { foo: 'a' },
}),
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 2,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
{ key: 'test', name: 'bar', params: { foo: 'a' } },
],
});
});
it('handles go back action', () => {
const router = StackRouter({});
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -477,9 +618,10 @@ it('handles go back action', () => {
it('handles pop action', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -650,9 +792,10 @@ it('handles pop action', () => {
it('handles pop to top action', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -684,9 +827,10 @@ it('handles pop to top action', () => {
it('replaces focused screen with replace', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -722,9 +866,10 @@ it('replaces focused screen with replace', () => {
it('replaces active screen with replace', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -763,9 +908,10 @@ it('replaces active screen with replace', () => {
it("doesn't handle replace if source key isn't present", () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -794,9 +940,10 @@ it("doesn't handle replace if source key isn't present", () => {
it("doesn't handle replace if screen to replace with isn't present", () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -824,11 +971,12 @@ it("doesn't handle replace if screen to replace with isn't present", () => {
it('handles push action', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {
baz: { foo: 21 },
},
routeGetIdList: {},
};
expect(
@@ -895,6 +1043,18 @@ it('handles push action', () => {
options
)
).toBe(null);
});
it('ensures unique ID for push', () => {
const router = StackRouter({});
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {
bar: ({ params }) => params?.foo,
qux: ({ params }) => params?.fux,
},
};
expect(
router.getStateForAction(
@@ -902,18 +1062,67 @@ it('handles push action', () => {
stale: false,
type: 'stack',
key: 'root',
index: 2,
index: 0,
routeNames: ['baz', 'bar', 'qux'],
routes: [{ key: 'bar', name: 'bar' }],
},
StackActions.push('bar', { foo: 'a' }),
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
});
expect(
router.getStateForAction(
{
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar-3', name: 'bar' },
{ key: 'bar-4', name: 'bar', params: { foo: 21 } },
{ key: 'baz-5', name: 'baz' },
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
},
StackActions.push('bar', { foo: 'a' }),
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
});
expect(
router.getStateForAction(
{
type: 'PUSH',
payload: { name: 'bar', key: 'bar-4', params: { bar: 29 } },
stale: false,
type: 'stack',
key: 'root',
index: 1,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
],
},
StackActions.push('bar', { foo: 'b' }),
options
)
).toEqual({
@@ -923,100 +1132,21 @@ it('handles push action', () => {
index: 2,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar-3', name: 'bar' },
{ key: 'baz-5', name: 'baz' },
{ key: 'bar-4', name: 'bar', params: { foo: 21, bar: 29 } },
{ key: 'bar', name: 'bar' },
{ key: 'bar-test', name: 'bar', params: { foo: 'a' } },
{ key: 'bar-test', name: 'bar', params: { foo: 'b' } },
],
});
expect(
router.getStateForAction(
{
stale: false,
type: 'stack',
key: 'root',
index: 2,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar-3', name: 'bar' },
{ key: 'bar-4', name: 'bar' },
{ key: 'baz-5', name: 'baz' },
],
},
{
type: 'PUSH',
payload: { name: 'bar', key: 'bar-6', params: { bar: 29 } },
},
options
)
).toEqual({
stale: false,
type: 'stack',
key: 'root',
index: 3,
routeNames: ['baz', 'bar', 'qux'],
routes: [
{ key: 'bar-3', name: 'bar' },
{ key: 'bar-4', name: 'bar' },
{ key: 'baz-5', name: 'baz' },
{ key: 'bar-6', name: 'bar', params: { bar: 29 } },
],
});
});
it('changes index on focus change', () => {
const router = StackRouter({});
expect(
router.getStateForRouteFocus(
{
index: 2,
key: 'stack-test',
routeNames: ['bar', 'baz', 'qux'],
routes: [
{ key: 'bar-0', name: 'bar' },
{ key: 'baz-0', name: 'baz' },
{ key: 'qux-0', name: 'qux' },
],
stale: false,
type: 'stack',
},
'baz-0'
)
).toEqual({
index: 1,
key: 'stack-test',
routeNames: ['bar', 'baz', 'qux'],
routes: [
{ key: 'bar-0', name: 'bar' },
{ key: 'baz-0', name: 'baz' },
],
stale: false,
type: 'stack',
});
const state = {
index: 0,
key: 'stack-test',
routeNames: ['bar', 'baz', 'qux'],
routes: [
{ key: 'bar-0', name: 'bar' },
{ key: 'baz-0', name: 'baz' },
],
stale: false as const,
type: 'stack' as const,
};
expect(router.getStateForRouteFocus(state, 'qux-0')).toEqual(state);
});
it("doesn't merge params on navigate to an existing screen", () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {
bar: { color: 'test' },
},
routeGetIdList: {},
};
expect(
@@ -1079,12 +1209,13 @@ it("doesn't merge params on navigate to an existing screen", () => {
it('merges params on navigate to an existing screen if merge: true', () => {
const router = StackRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {
bar: { color: 'test' },
baz: { foo: 12 },
},
routeGetIdList: {},
};
expect(

View File

@@ -4,6 +4,7 @@ import {
TabActions,
TabNavigationState,
ParamListBase,
RouterConfigOptions,
} from '..';
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
@@ -18,6 +19,7 @@ it('gets initial state from route names and params with initialRouteName', () =>
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
})
).toEqual({
index: 1,
@@ -47,6 +49,7 @@ it('gets initial state from route names and params without initialRouteName', ()
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
})
).toEqual({
index: 0,
@@ -66,12 +69,13 @@ it('gets initial state from route names and params without initialRouteName', ()
it('gets rehydrated state from partial state', () => {
const router = TabRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {
baz: { answer: 42 },
qux: { name: 'Jane' },
},
routeGetIdList: {},
};
expect(
@@ -250,6 +254,7 @@ it("doesn't rehydrate state if it's not stale", () => {
router.getRehydratedState(state, {
routeNames: [],
routeParamList: {},
routeGetIdList: {},
})
).toBe(state);
});
@@ -257,9 +262,10 @@ it("doesn't rehydrate state if it's not stale", () => {
it('restores correct history on rehydrating with backBehavior: order', () => {
const router = TabRouter({ backBehavior: 'order' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -298,9 +304,10 @@ it('restores correct history on rehydrating with backBehavior: order', () => {
it('restores correct history on rehydrating with backBehavior: history', () => {
const router = TabRouter({ backBehavior: 'history' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -338,9 +345,10 @@ it('restores correct history on rehydrating with backBehavior: firstRoute', () =
initialRouteName: 'bar',
});
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -381,9 +389,10 @@ it('restores correct history on rehydrating with backBehavior: initialRoute', ()
initialRouteName: 'bar',
});
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -421,9 +430,10 @@ it('restores correct history on rehydrating with backBehavior: initialRoute', ()
it('restores correct history on rehydrating with backBehavior: none', () => {
const router = TabRouter({ backBehavior: 'none' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['foo', 'bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -479,6 +489,7 @@ it('gets state on route names change', () => {
qux: { name: 'John' },
fiz: { fruit: 'apple' },
},
routeGetIdList: {},
}
)
).toEqual({
@@ -513,6 +524,7 @@ it('gets state on route names change', () => {
{
routeNames: ['foo', 'fiz'],
routeParamList: {},
routeGetIdList: {},
}
)
).toEqual({
@@ -553,6 +565,7 @@ it('preserves focused route on route names change', () => {
qux: { name: 'John' },
fiz: { fruit: 'apple' },
},
routeGetIdList: {},
}
)
).toEqual({
@@ -595,6 +608,7 @@ it('falls back to first route if route is removed on route names change', () =>
qux: { name: 'John' },
fiz: { fruit: 'apple' },
},
routeGetIdList: {},
}
)
).toEqual({
@@ -614,9 +628,10 @@ it('falls back to first route if route is removed on route names change', () =>
it('handles navigate action', () => {
const router = TabRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -704,9 +719,10 @@ it('handles navigate action', () => {
it('handles jump to action', () => {
const router = TabRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -745,9 +761,10 @@ it('handles jump to action', () => {
it('handles back action with backBehavior: history', () => {
const router = TabRouter({ backBehavior: 'history' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
let state = router.getInitialState(options);
@@ -831,9 +848,10 @@ it('handles back action with backBehavior: history', () => {
it('handles back action with backBehavior: order', () => {
const router = TabRouter({ backBehavior: 'order' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
let state = router.getInitialState(options);
@@ -902,9 +920,10 @@ it('handles back action with backBehavior: order', () => {
it('handles back action with backBehavior: initialRoute', () => {
const router = TabRouter({ backBehavior: 'initialRoute' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
let state = router.getInitialState(options);
@@ -974,9 +993,10 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
initialRouteName: 'baz',
});
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
let state = router.getInitialState(options);
@@ -1042,9 +1062,10 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
it('handles back action with backBehavior: none', () => {
const router = TabRouter({ backBehavior: 'none' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
let state = router.getInitialState(options);
@@ -1062,9 +1083,10 @@ it('handles back action with backBehavior: none', () => {
it('updates route key history on navigate and jump to', () => {
const router = TabRouter({ backBehavior: 'history' });
const options = {
const options: RouterConfigOptions = {
routeNames: ['bar', 'baz', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
let state: TabNavigationState<ParamListBase> = {
@@ -1165,11 +1187,12 @@ it('updates route key history on focus change', () => {
it("doesn't merge params on navigate to an existing screen", () => {
const router = TabRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {
qux: { color: 'indigo' },
},
routeGetIdList: {},
};
expect(
@@ -1280,9 +1303,10 @@ it("doesn't merge params on navigate to an existing screen", () => {
it('merges params on navigate to an existing screen if merge: true', () => {
const router = TabRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(
@@ -1365,9 +1389,10 @@ it('merges params on navigate to an existing screen if merge: true', () => {
it("doesn't merge params on jump to an existing screen", () => {
const router = TabRouter({});
const options = {
const options: RouterConfigOptions = {
routeNames: ['baz', 'bar', 'qux'],
routeParamList: {},
routeGetIdList: {},
};
expect(

View File

@@ -132,6 +132,11 @@ export type RouterFactory<
export type RouterConfigOptions = {
routeNames: string[];
routeParamList: ParamListBase;
routeGetIdList: Record<
string,
| ((options: { params?: Record<string, any> }) => string | undefined)
| undefined
>;
};
export type Router<