Skip to content

Commit 63eddd5

Browse files
thymikeesourcecode911
authored andcommitted
feat(context): refactor passing navigation context (react-navigation#3668)
* feat(context): refactor passing navigation context * remove commented code in example * adjust src/views/withNavigationFocus.js * refactor stuff * extract scene to variable * Add test * Apply CR comments * remove junk * bring back screen mode header
1 parent 7bda6d3 commit 63eddd5

File tree

9 files changed

+108
-45
lines changed

9 files changed

+108
-45
lines changed

examples/NavigationPlayground/yarn.lock

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,13 @@ create-react-class@^15.5.2:
16591659
loose-envify "^1.3.1"
16601660
object-assign "^4.1.1"
16611661

1662+
create-react-context@^0.2.1:
1663+
version "0.2.1"
1664+
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.1.tgz#425a3d96f4b7690c2fbf20aed5aeae2e2007a959"
1665+
dependencies:
1666+
fbjs "^0.8.0"
1667+
gud "^1.0.0"
1668+
16621669
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
16631670
version "5.1.0"
16641671
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -2247,7 +2254,7 @@ fbjs-scripts@^0.8.1:
22472254
semver "^5.1.0"
22482255
through2 "^2.0.0"
22492256

2250-
fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9:
2257+
fbjs@^0.8.0, fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9:
22512258
version "0.8.16"
22522259
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
22532260
dependencies:
@@ -2625,6 +2632,10 @@ growly@^1.3.0:
26252632
version "1.3.0"
26262633
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
26272634

2635+
gud@^1.0.0:
2636+
version "1.0.0"
2637+
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
2638+
26282639
gulp-util@^3.0.4:
26292640
version "3.0.8"
26302641
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
},
3131
"dependencies": {
3232
"clamp": "^1.0.1",
33+
"create-react-context": "^0.2.1",
3334
"hoist-non-react-statics": "^2.2.0",
3435
"path-to-regexp": "^1.7.0",
3536
"prop-types": "^15.5.10",

src/navigators/__tests__/StackNavigator-test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { StyleSheet, View } from 'react-native';
33
import renderer from 'react-test-renderer';
44

55
import StackNavigator from '../createStackNavigator';
6+
import withNavigation from '../../views/withNavigation';
67

78
const styles = StyleSheet.create({
89
header: {
@@ -51,4 +52,37 @@ describe('StackNavigator', () => {
5152

5253
expect(rendered).toMatchSnapshot();
5354
});
55+
56+
it('passes navigation to headerRight when wrapped in withNavigation', () => {
57+
const spy = jest.fn();
58+
59+
class TestComponent extends React.Component {
60+
render() {
61+
return <View>{this.props.onPress(this.props.navigation)}</View>;
62+
}
63+
}
64+
65+
const TestComponentWithNavigation = withNavigation(TestComponent);
66+
67+
class A extends React.Component {
68+
static navigationOptions = {
69+
headerRight: <TestComponentWithNavigation onPress={spy} />,
70+
};
71+
72+
render() {
73+
return <View />;
74+
}
75+
}
76+
77+
const Nav = StackNavigator({ A: { screen: A } });
78+
79+
renderer.create(<Nav />);
80+
81+
expect(spy).toBeCalledWith(
82+
expect.objectContaining({
83+
navigate: expect.any(Function),
84+
addListener: expect.any(Function),
85+
})
86+
);
87+
});
5488
});

src/views/NavigationContext.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import propTypes from 'prop-types';
3+
import createReactContext from 'create-react-context';
4+
5+
const NavigationContext = createReactContext();
6+
7+
export const NavigationProvider = NavigationContext.Provider;
8+
export const NavigationConsumer = NavigationContext.Consumer;

src/views/SceneView.js

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
import React from 'react';
22
import propTypes from 'prop-types';
3+
import { NavigationProvider } from './NavigationContext';
34

45
export default class SceneView extends React.PureComponent {
5-
static childContextTypes = {
6-
navigation: propTypes.object.isRequired,
7-
};
8-
9-
getChildContext() {
10-
return {
11-
navigation: this.props.navigation,
12-
};
13-
}
14-
156
render() {
16-
const { screenProps, navigation, component: Component } = this.props;
17-
return <Component screenProps={screenProps} navigation={navigation} />;
7+
const { screenProps, component: Component, navigation } = this.props;
8+
return (
9+
<NavigationProvider value={navigation}>
10+
<Component screenProps={screenProps} navigation={navigation} />
11+
</NavigationProvider>
12+
);
1813
}
1914
}

src/views/StackView/StackViewLayout.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Card from './StackViewCard';
1515
import Header from '../Header/Header';
1616
import NavigationActions from '../../NavigationActions';
1717
import SceneView from '../SceneView';
18+
import { NavigationProvider } from '../NavigationContext';
1819

1920
import TransitionConfigs from './StackViewTransitionConfigs';
2021
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
@@ -194,9 +195,11 @@ class StackViewLayout extends React.Component {
194195
let floatingHeader = null;
195196
const headerMode = this._getHeaderMode();
196197
if (headerMode === 'float') {
197-
floatingHeader = this._renderHeader(
198-
this.props.transitionProps.scene,
199-
headerMode
198+
const { scene } = this.props.transitionProps;
199+
floatingHeader = (
200+
<NavigationProvider value={scene.descriptor.navigation}>
201+
{this._renderHeader(scene, headerMode)}
202+
</NavigationProvider>
200203
);
201204
}
202205
const {

src/views/withNavigation.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
import React from 'react';
22
import propTypes from 'prop-types';
33
import hoistStatics from 'hoist-non-react-statics';
4+
import invariant from '../utils/invariant';
5+
import { NavigationConsumer } from './NavigationContext';
46

57
export default function withNavigation(Component) {
68
class ComponentWithNavigation extends React.Component {
79
static displayName = `withNavigation(${Component.displayName ||
810
Component.name})`;
911

10-
static contextTypes = {
11-
navigation: propTypes.object.isRequired,
12-
};
13-
1412
render() {
15-
const { navigation } = this.context;
1613
return (
17-
<Component
18-
{...this.props}
19-
navigation={navigation}
20-
ref={this.props.onRef}
21-
/>
14+
<NavigationConsumer>
15+
{navigation => {
16+
invariant(
17+
!!navigation,
18+
'withNavigationFocus can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.'
19+
);
20+
return (
21+
<Component
22+
{...this.props}
23+
navigation={navigation}
24+
ref={this.props.onRef}
25+
/>
26+
);
27+
}}
28+
</NavigationConsumer>
2229
);
2330
}
2431
}

src/views/withNavigationFocus.js

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,28 @@ import React from 'react';
22
import propTypes from 'prop-types';
33
import hoistStatics from 'hoist-non-react-statics';
44
import invariant from '../utils/invariant';
5+
import withNavigation from './withNavigation';
56

67
export default function withNavigationFocus(Component) {
78
class ComponentWithNavigationFocus extends React.Component {
89
static displayName = `withNavigationFocus(${Component.displayName ||
910
Component.name})`;
1011

11-
static contextTypes = {
12-
navigation: propTypes.object.isRequired,
13-
};
14-
15-
constructor(props, context) {
16-
super();
12+
constructor(props) {
13+
super(props);
1714

1815
this.state = {
19-
isFocused: this.getNavigation(props, context).isFocused(),
16+
isFocused: props.navigation ? props.navigation.isFocused() : false,
2017
};
2118
}
2219

2320
componentDidMount() {
24-
const navigation = this.getNavigation();
21+
const { navigation } = this.props;
22+
invariant(
23+
!!navigation,
24+
'withNavigationFocus can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.'
25+
);
26+
2527
this.subscriptions = [
2628
navigation.addListener('didFocus', () =>
2729
this.setState({ isFocused: true })
@@ -36,15 +38,6 @@ export default function withNavigationFocus(Component) {
3638
this.subscriptions.forEach(sub => sub.remove());
3739
}
3840

39-
getNavigation = (props = this.props, context = this.context) => {
40-
const navigation = props.navigation || context.navigation;
41-
invariant(
42-
!!navigation,
43-
'withNavigationFocus can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.'
44-
);
45-
return navigation;
46-
};
47-
4841
render() {
4942
return (
5043
<Component
@@ -56,5 +49,5 @@ export default function withNavigationFocus(Component) {
5649
}
5750
}
5851

59-
return hoistStatics(ComponentWithNavigationFocus, Component);
52+
return hoistStatics(withNavigation(ComponentWithNavigationFocus), Component);
6053
}

yarn.lock

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,13 @@ create-react-class@^15.5.2:
14901490
loose-envify "^1.3.1"
14911491
object-assign "^4.1.1"
14921492

1493+
create-react-context@^0.2.1:
1494+
version "0.2.1"
1495+
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.1.tgz#425a3d96f4b7690c2fbf20aed5aeae2e2007a959"
1496+
dependencies:
1497+
fbjs "^0.8.0"
1498+
gud "^1.0.0"
1499+
14931500
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
14941501
version "5.1.0"
14951502
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -2085,7 +2092,7 @@ fbjs-scripts@^0.8.1:
20852092
semver "^5.1.0"
20862093
through2 "^2.0.0"
20872094

2088-
fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
2095+
fbjs@^0.8.0, fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
20892096
version "0.8.16"
20902097
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
20912098
dependencies:
@@ -2386,6 +2393,10 @@ growly@^1.3.0:
23862393
version "1.3.0"
23872394
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
23882395

2396+
gud@^1.0.0:
2397+
version "1.0.0"
2398+
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
2399+
23892400
gulp-util@^3.0.4:
23902401
version "3.0.8"
23912402
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"

0 commit comments

Comments
 (0)