Skip to content
This repository was archived by the owner on Nov 3, 2025. It is now read-only.

Commit 6742265

Browse files
committed
✨ (NavigationContext) Exposes NavigationConsumer, withNavigation and useNavigation utils
1 parent 2743654 commit 6742265

10 files changed

Lines changed: 119 additions & 20 deletions

example/babel.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module.exports = {
22
presets: ['module:metro-react-native-babel-preset', '@babel/preset-typescript'],
3+
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
34
};

example/metro.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ module.exports = {
2121
'mobx-react': path.resolve(__dirname, '../node_modules/mobx-react'),
2222
react: path.resolve(__dirname, '../node_modules/react'),
2323
'react-native': path.resolve(__dirname, '../node_modules/react-native'),
24+
'@babel/plugin-proposal-decorators': path.resolve(
25+
__dirname,
26+
'../node_modules/@babel/plugin-proposal-decorators'
27+
),
2428
},
2529
},
2630
transformer: {

src/Navigation.store.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,17 @@ interface ICanalsMap {
77
}
88

99
export class Navigation {
10+
static getInstance() {
11+
if (Navigation.instance) {
12+
return Navigation.instance;
13+
}
14+
Navigation.instance = new Navigation();
15+
return Navigation.instance;
16+
}
17+
private static instance: Navigation;
18+
19+
private canalsMap: ICanalsMap = {};
20+
1021
@observable
11-
state: INavigationState = {};
12-
canalsMap: ICanalsMap = {};
22+
private state: INavigationState = {};
1323
}

src/NavigationContext.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1-
import { createContext } from 'react';
1+
import React, { Component, createContext, useContext, ComponentType } from 'react';
22
import { Navigation } from './Navigation.store';
33

4-
export const NavigationContext = createContext(new Navigation());
4+
const NavigationContext = createContext(Navigation.getInstance());
5+
6+
export const useNavigation = () => useContext(NavigationContext);
7+
8+
export const NavigationConsumer = NavigationContext.Consumer;
9+
10+
export const withNavigation = (
11+
ComponentWithoutNavigation: ComponentType<{ navigation: Navigation }>
12+
) =>
13+
class ComponentWithNavigation extends Component {
14+
static displayName = 'withNavigation';
15+
render() {
16+
return (
17+
<NavigationConsumer>
18+
{navigation => <ComponentWithoutNavigation {...this.props} navigation={navigation} />}
19+
</NavigationConsumer>
20+
);
21+
}
22+
};

src/__tests__/Navigation.store.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
import { Navigation } from '../Navigation.store';
22

33
describe('Navigation store', () => {
4+
it('exposes the store instance', () => {
5+
const expectedNavigation = new Navigation();
6+
const navigation = Navigation.getInstance();
7+
expect(navigation).toEqual(expectedNavigation);
8+
});
9+
10+
it('exposes the same store instance if it accessed twice', () => {
11+
const navigation = Navigation.getInstance();
12+
const otherNavigation = Navigation.getInstance();
13+
expect(otherNavigation).toBe(navigation);
14+
});
15+
416
it('initializes with an empty state and canalsMap', () => {
517
const navigation = new Navigation();
18+
// @ts-ignore
619
expect(navigation.state).toEqual({});
20+
// @ts-ignore
721
expect(navigation.canalsMap).toEqual({});
822
});
923

1024
describe('state', () => {
1125
it('cannot be changed directly', () => {
1226
try {
1327
const navigation = new Navigation();
28+
// @ts-ignore
1429
navigation.state = {};
1530
} catch (error) {
1631
expect(error.message).toMatch(/\[mobx\]/);

src/__tests__/NavigationContext.test.tsx

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,47 @@ import React from 'react';
22
import TestRenderer from 'react-test-renderer';
33
import { View } from 'react-native';
44

5-
import { NavigationContext } from '../NavigationContext';
5+
import { NavigationConsumer, withNavigation, useNavigation } from '../NavigationContext';
66
import { Navigation } from '../Navigation.store';
77

88
describe('NavigationContext', () => {
9-
it('initializes a context with a new navigation store', () => {
10-
const expectedNavigation = new Navigation();
11-
TestRenderer.create(
12-
<NavigationContext.Consumer>
13-
{navigation => {
14-
expect(navigation).toEqual(expectedNavigation);
15-
return <View />;
16-
}}
17-
</NavigationContext.Consumer>
18-
);
19-
expect.assertions(1);
9+
describe('NavigationConsumer', () => {
10+
it('connects to context and exposes the navigation prop', () => {
11+
const expectedNavigation = new Navigation();
12+
TestRenderer.create(
13+
<NavigationConsumer>
14+
{navigation => {
15+
expect(navigation).toEqual(expectedNavigation);
16+
return <View />;
17+
}}
18+
</NavigationConsumer>
19+
);
20+
expect.assertions(1);
21+
});
22+
});
23+
describe('withNavigation', () => {
24+
it('connects to context and exposes the navigation prop', () => {
25+
const expectedNavigation = new Navigation();
26+
// @ts-ignore
27+
const ViewWithNavigation = withNavigation(View);
28+
const testRenderer = TestRenderer.create(<ViewWithNavigation />);
29+
// @ts-ignore
30+
expect(testRenderer.root.children[0].props.navigation).toEqual(expectedNavigation);
31+
expect.assertions(1);
32+
});
33+
});
34+
describe('useNavigation', () => {
35+
it('connects to context and exposes the navigation prop', () => {
36+
const expectedNavigation = new Navigation();
37+
const ViewWithNavigation = () => {
38+
const navigation = useNavigation();
39+
// @ts-ignore
40+
return <View navigation={navigation} />;
41+
};
42+
const testRenderer = TestRenderer.create(<ViewWithNavigation />);
43+
// @ts-ignore
44+
expect(testRenderer.root.children[0].props.navigation).toEqual(expectedNavigation);
45+
expect.assertions(1);
46+
});
2047
});
2148
});
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`createCanal renders the first page when mounted 1`] = `<View />`;
3+
exports[`createCanal renders the first page when mounted 1`] = `
4+
<View
5+
navigation={
6+
Navigation {
7+
"canalsMap": Object {},
8+
}
9+
}
10+
/>
11+
`;

src/createCanal.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import React, { ComponentType, FunctionComponent } from 'react';
2+
import { Navigation } from 'Navigation.store';
3+
import { useNavigation } from './NavigationContext';
24

3-
export const createCanal = (...Pages: ComponentType[]): FunctionComponent => {
5+
export const createCanal = (
6+
...Pages: Array<ComponentType<{ navigation: Navigation }>>
7+
): FunctionComponent => {
48
for (let index = 0; index < Pages.length; index++) {
59
const Page = Pages[index];
610
if (!(React.isValidElement(Page) || typeof Page === 'function')) {
@@ -11,5 +15,9 @@ export const createCanal = (...Pages: ComponentType[]): FunctionComponent => {
1115
}
1216
}
1317
const FirstPage = Pages[0];
14-
return () => <FirstPage />;
18+
const Canal = () => {
19+
const navigation = useNavigation();
20+
return <FirstPage navigation={navigation} />;
21+
};
22+
return Canal;
1523
};

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { createCanal } from './createCanal';
2+
export { NavigationConsumer, withNavigation, useNavigation } from './NavigationContext';

tsconfig.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,12 @@
2828
"types": ["react-native"]
2929
},
3030
"include": ["./src"],
31-
"exclude": ["node_modules", "dist", "example", "**/__tests__/*"]
31+
"exclude": [
32+
"node_modules",
33+
"dist",
34+
"example",
35+
"**/__tests__/*",
36+
"**/*.test.ts",
37+
"**/*.test.tsx"
38+
]
3239
}

0 commit comments

Comments
 (0)