feat: handle screens nested in React.Fragment

This commit is contained in:
satyajit.happy
2019-07-18 16:49:58 +02:00
committed by Satyajit Sahoo
parent 637263e9cf
commit 6955ae9d25
2 changed files with 71 additions and 21 deletions

View File

@@ -189,6 +189,51 @@ it('rehydrates state for a navigator on navigation', () => {
});
});
it('initializes state for nested screens in React.Fragment', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const TestScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch({ type: 'UPDATE' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo" component={TestScreen} />
<React.Fragment>
<Screen name="bar" component={jest.fn()} />
<Screen name="baz" component={jest.fn()} />
</React.Fragment>
</TestNavigator>
</NavigationContainer>
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).toBeCalledWith({
index: 0,
key: '0',
routeNames: ['foo', 'bar', 'baz'],
routes: [
{ key: 'foo', name: 'foo' },
{ key: 'bar', name: 'bar' },
{ key: 'baz', name: 'baz' },
],
});
});
it('initializes state for nested navigator on navigation', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);

View File

@@ -17,19 +17,18 @@ type Options = {
const isArrayEqual = (a: any[], b: any[]) =>
a.length === b.length && a.every((it, index) => it === b[index]);
export default function useNavigationBuilder(
router: Router<any>,
options: Options
) {
useRegisterNavigator();
const getRouteConfigsFromChildren = (children: React.ReactNode) =>
React.Children.toArray(children).reduce<RouteConfig[]>((acc, child) => {
if (React.isValidElement(child)) {
if (child.type === Screen) {
acc.push(child.props as RouteConfig);
return acc;
}
const screens = React.Children.map(options.children, child => {
if (child === null || child === undefined) {
return;
}
if (React.isValidElement(child) && child.type === Screen) {
return child.props as RouteConfig;
if (child.type === React.Fragment) {
acc.push(...getRouteConfigsFromChildren(child.props.children));
return acc;
}
}
throw new Error(
@@ -38,15 +37,21 @@ export default function useNavigationBuilder(
child.type && child.type.name ? child.type.name : String(child)
}')`
);
})
.filter(Boolean)
.reduce(
(acc, curr) => {
acc[curr!.name] = curr as RouteConfig;
return acc;
},
{} as { [key: string]: RouteConfig }
);
}, []);
export default function useNavigationBuilder(
router: Router<any>,
options: Options
) {
useRegisterNavigator();
const screens = getRouteConfigsFromChildren(options.children).reduce(
(acc, curr) => {
acc[curr.name] = curr;
return acc;
},
{} as { [key: string]: RouteConfig }
);
const routeNames = Object.keys(screens);
const initialRouteName =