mirror of
https://github.com/status-im/react-navigation.git
synced 2025-02-24 00:58:20 +00:00
feat(context): refactor passing navigation context (#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
This commit is contained in:
parent
fd75e9c14c
commit
fcbf78e658
@ -1659,6 +1659,13 @@ create-react-class@^15.5.2:
|
|||||||
loose-envify "^1.3.1"
|
loose-envify "^1.3.1"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
create-react-context@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.1.tgz#425a3d96f4b7690c2fbf20aed5aeae2e2007a959"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.0"
|
||||||
|
gud "^1.0.0"
|
||||||
|
|
||||||
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||||
@ -2247,7 +2254,7 @@ fbjs-scripts@^0.8.1:
|
|||||||
semver "^5.1.0"
|
semver "^5.1.0"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9:
|
fbjs@^0.8.0, fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9:
|
||||||
version "0.8.16"
|
version "0.8.16"
|
||||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2625,6 +2632,10 @@ growly@^1.3.0:
|
|||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||||
|
|
||||||
|
gud@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
|
||||||
|
|
||||||
gulp-util@^3.0.4:
|
gulp-util@^3.0.4:
|
||||||
version "3.0.8"
|
version "3.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"
|
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clamp": "^1.0.1",
|
"clamp": "^1.0.1",
|
||||||
|
"create-react-context": "^0.2.1",
|
||||||
"hoist-non-react-statics": "^2.2.0",
|
"hoist-non-react-statics": "^2.2.0",
|
||||||
"path-to-regexp": "^1.7.0",
|
"path-to-regexp": "^1.7.0",
|
||||||
"prop-types": "^15.5.10",
|
"prop-types": "^15.5.10",
|
||||||
|
@ -3,6 +3,7 @@ import { StyleSheet, View } from 'react-native';
|
|||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
import StackNavigator from '../createStackNavigator';
|
import StackNavigator from '../createStackNavigator';
|
||||||
|
import withNavigation from '../../views/withNavigation';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
header: {
|
header: {
|
||||||
@ -51,4 +52,37 @@ describe('StackNavigator', () => {
|
|||||||
|
|
||||||
expect(rendered).toMatchSnapshot();
|
expect(rendered).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes navigation to headerRight when wrapped in withNavigation', () => {
|
||||||
|
const spy = jest.fn();
|
||||||
|
|
||||||
|
class TestComponent extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <View>{this.props.onPress(this.props.navigation)}</View>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TestComponentWithNavigation = withNavigation(TestComponent);
|
||||||
|
|
||||||
|
class A extends React.Component {
|
||||||
|
static navigationOptions = {
|
||||||
|
headerRight: <TestComponentWithNavigation onPress={spy} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Nav = StackNavigator({ A: { screen: A } });
|
||||||
|
|
||||||
|
renderer.create(<Nav />);
|
||||||
|
|
||||||
|
expect(spy).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
navigate: expect.any(Function),
|
||||||
|
addListener: expect.any(Function),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
8
src/views/NavigationContext.js
Normal file
8
src/views/NavigationContext.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import propTypes from 'prop-types';
|
||||||
|
import createReactContext from 'create-react-context';
|
||||||
|
|
||||||
|
const NavigationContext = createReactContext();
|
||||||
|
|
||||||
|
export const NavigationProvider = NavigationContext.Provider;
|
||||||
|
export const NavigationConsumer = NavigationContext.Consumer;
|
@ -1,19 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import propTypes from 'prop-types';
|
import propTypes from 'prop-types';
|
||||||
|
import { NavigationProvider } from './NavigationContext';
|
||||||
|
|
||||||
export default class SceneView extends React.PureComponent {
|
export default class SceneView extends React.PureComponent {
|
||||||
static childContextTypes = {
|
|
||||||
navigation: propTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
getChildContext() {
|
|
||||||
return {
|
|
||||||
navigation: this.props.navigation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { screenProps, navigation, component: Component } = this.props;
|
const { screenProps, component: Component, navigation } = this.props;
|
||||||
return <Component screenProps={screenProps} navigation={navigation} />;
|
return (
|
||||||
|
<NavigationProvider value={navigation}>
|
||||||
|
<Component screenProps={screenProps} navigation={navigation} />
|
||||||
|
</NavigationProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import Card from './StackViewCard';
|
|||||||
import Header from '../Header/Header';
|
import Header from '../Header/Header';
|
||||||
import NavigationActions from '../../NavigationActions';
|
import NavigationActions from '../../NavigationActions';
|
||||||
import SceneView from '../SceneView';
|
import SceneView from '../SceneView';
|
||||||
|
import { NavigationProvider } from '../NavigationContext';
|
||||||
|
|
||||||
import TransitionConfigs from './StackViewTransitionConfigs';
|
import TransitionConfigs from './StackViewTransitionConfigs';
|
||||||
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
||||||
@ -194,9 +195,11 @@ class StackViewLayout extends React.Component {
|
|||||||
let floatingHeader = null;
|
let floatingHeader = null;
|
||||||
const headerMode = this._getHeaderMode();
|
const headerMode = this._getHeaderMode();
|
||||||
if (headerMode === 'float') {
|
if (headerMode === 'float') {
|
||||||
floatingHeader = this._renderHeader(
|
const { scene } = this.props.transitionProps;
|
||||||
this.props.transitionProps.scene,
|
floatingHeader = (
|
||||||
headerMode
|
<NavigationProvider value={scene.descriptor.navigation}>
|
||||||
|
{this._renderHeader(scene, headerMode)}
|
||||||
|
</NavigationProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import propTypes from 'prop-types';
|
import propTypes from 'prop-types';
|
||||||
import hoistStatics from 'hoist-non-react-statics';
|
import hoistStatics from 'hoist-non-react-statics';
|
||||||
|
import invariant from '../utils/invariant';
|
||||||
|
import { NavigationConsumer } from './NavigationContext';
|
||||||
|
|
||||||
export default function withNavigation(Component) {
|
export default function withNavigation(Component) {
|
||||||
class ComponentWithNavigation extends React.Component {
|
class ComponentWithNavigation extends React.Component {
|
||||||
static displayName = `withNavigation(${Component.displayName ||
|
static displayName = `withNavigation(${Component.displayName ||
|
||||||
Component.name})`;
|
Component.name})`;
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
navigation: propTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navigation } = this.context;
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<NavigationConsumer>
|
||||||
{...this.props}
|
{navigation => {
|
||||||
navigation={navigation}
|
invariant(
|
||||||
ref={this.props.onRef}
|
!!navigation,
|
||||||
/>
|
'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.'
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Component
|
||||||
|
{...this.props}
|
||||||
|
navigation={navigation}
|
||||||
|
ref={this.props.onRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</NavigationConsumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,28 @@ import React from 'react';
|
|||||||
import propTypes from 'prop-types';
|
import propTypes from 'prop-types';
|
||||||
import hoistStatics from 'hoist-non-react-statics';
|
import hoistStatics from 'hoist-non-react-statics';
|
||||||
import invariant from '../utils/invariant';
|
import invariant from '../utils/invariant';
|
||||||
|
import withNavigation from './withNavigation';
|
||||||
|
|
||||||
export default function withNavigationFocus(Component) {
|
export default function withNavigationFocus(Component) {
|
||||||
class ComponentWithNavigationFocus extends React.Component {
|
class ComponentWithNavigationFocus extends React.Component {
|
||||||
static displayName = `withNavigationFocus(${Component.displayName ||
|
static displayName = `withNavigationFocus(${Component.displayName ||
|
||||||
Component.name})`;
|
Component.name})`;
|
||||||
|
|
||||||
static contextTypes = {
|
constructor(props) {
|
||||||
navigation: propTypes.object.isRequired,
|
super(props);
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isFocused: this.getNavigation(props, context).isFocused(),
|
isFocused: props.navigation ? props.navigation.isFocused() : false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const navigation = this.getNavigation();
|
const { navigation } = this.props;
|
||||||
|
invariant(
|
||||||
|
!!navigation,
|
||||||
|
'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.'
|
||||||
|
);
|
||||||
|
|
||||||
this.subscriptions = [
|
this.subscriptions = [
|
||||||
navigation.addListener('didFocus', () =>
|
navigation.addListener('didFocus', () =>
|
||||||
this.setState({ isFocused: true })
|
this.setState({ isFocused: true })
|
||||||
@ -36,15 +38,6 @@ export default function withNavigationFocus(Component) {
|
|||||||
this.subscriptions.forEach(sub => sub.remove());
|
this.subscriptions.forEach(sub => sub.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
getNavigation = (props = this.props, context = this.context) => {
|
|
||||||
const navigation = props.navigation || context.navigation;
|
|
||||||
invariant(
|
|
||||||
!!navigation,
|
|
||||||
'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.'
|
|
||||||
);
|
|
||||||
return navigation;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
@ -56,5 +49,5 @@ export default function withNavigationFocus(Component) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hoistStatics(ComponentWithNavigationFocus, Component);
|
return hoistStatics(withNavigation(ComponentWithNavigationFocus), Component);
|
||||||
}
|
}
|
||||||
|
13
yarn.lock
13
yarn.lock
@ -1490,6 +1490,13 @@ create-react-class@^15.5.2:
|
|||||||
loose-envify "^1.3.1"
|
loose-envify "^1.3.1"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
create-react-context@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.1.tgz#425a3d96f4b7690c2fbf20aed5aeae2e2007a959"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.0"
|
||||||
|
gud "^1.0.0"
|
||||||
|
|
||||||
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||||
@ -2085,7 +2092,7 @@ fbjs-scripts@^0.8.1:
|
|||||||
semver "^5.1.0"
|
semver "^5.1.0"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
|
fbjs@^0.8.0, fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
|
||||||
version "0.8.16"
|
version "0.8.16"
|
||||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2386,6 +2393,10 @@ growly@^1.3.0:
|
|||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||||
|
|
||||||
|
gud@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
|
||||||
|
|
||||||
gulp-util@^3.0.4:
|
gulp-util@^3.0.4:
|
||||||
version "3.0.8"
|
version "3.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"
|
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user