From c74f001b1ce63243cc6e3ab3c346ea5d531319f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Sat, 17 Feb 2018 01:57:33 +0100 Subject: [PATCH] add withNavigationFocus HOC (#3512) * add withNavigationFocus HOC See: - https://github.com/react-navigation/react-navigation/issues/51#issuecomment-276003658 - https://github.com/react-navigation/react-navigation/issues/51#issuecomment-278761705 - https://github.com/react-navigation/react-navigation/pull/3345#issuecomment-360260067 * typos * remove unused import * Add withNavigationFocus export * add example TabsWithNavigationFocus * add example TabsWithNavigationFocus * withNavigationFocus: get navigation from context or props * subs => subscriptions * fix flow issues --- examples/NavigationPlayground/js/App.js | 7 +++ .../js/TabsWithNavigationFocus.js | 48 ++++++++++++++++ flow/react-navigation.js | 3 + src/react-navigation.js | 3 + src/react-navigation.web.js | 3 + src/views/withNavigationFocus.js | 56 +++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 examples/NavigationPlayground/js/TabsWithNavigationFocus.js create mode 100644 src/views/withNavigationFocus.js diff --git a/examples/NavigationPlayground/js/App.js b/examples/NavigationPlayground/js/App.js index ac4cb796..a55c7e73 100644 --- a/examples/NavigationPlayground/js/App.js +++ b/examples/NavigationPlayground/js/App.js @@ -31,6 +31,7 @@ import SimpleStack from './SimpleStack'; import StackWithHeaderPreset from './StackWithHeaderPreset'; import SimpleTabs from './SimpleTabs'; import TabAnimations from './TabAnimations'; +import TabsWithNavigationFocus from './TabsWithNavigationFocus'; const ExampleInfo = { SimpleStack: { @@ -99,7 +100,12 @@ const ExampleInfo = { name: 'Animated Tabs Example', description: 'Tab transitions have custom animations', }, + TabsWithNavigationFocus: { + name: 'Tabs using withNavigationFocus HOC', + description: 'Tab views will receive isFocused prop from HOC', + }, }; + const ExampleRoutes = { SimpleStack: SimpleStack, SimpleTabs: SimpleTabs, @@ -124,6 +130,7 @@ const ExampleRoutes = { path: 'settings', }, TabAnimations: TabAnimations, + TabsWithNavigationFocus: TabsWithNavigationFocus, }; type State = { diff --git a/examples/NavigationPlayground/js/TabsWithNavigationFocus.js b/examples/NavigationPlayground/js/TabsWithNavigationFocus.js new file mode 100644 index 00000000..662269b3 --- /dev/null +++ b/examples/NavigationPlayground/js/TabsWithNavigationFocus.js @@ -0,0 +1,48 @@ +/** + * @flow + */ + +import React from 'react'; +import { SafeAreaView } from 'react-native'; +import { TabNavigator, withNavigationFocus } from 'react-navigation'; + +import SampleText from './SampleText'; + +const createTabScreen = name => { + const TabScreen = ({ isFocused }) => ( + + {'Tab=' + name} + {'IsFocused=' + isFocused ? 'true' : 'false'} + + ); + + return withNavigationFocus(TabScreen); +}; + +const TabsWithNavigationFocus = TabNavigator( + { + One: { + screen: createTabScreen('One'), + }, + Two: { + screen: createTabScreen('Two'), + }, + Three: { + screen: createTabScreen('Three'), + }, + }, + { + tabBarPosition: 'bottom', + animationEnabled: true, + swipeEnabled: true, + } +); + +export default TabsWithNavigationFocus; diff --git a/flow/react-navigation.js b/flow/react-navigation.js index 8b81a16b..7a43facf 100644 --- a/flow/react-navigation.js +++ b/flow/react-navigation.js @@ -1103,4 +1103,7 @@ declare module 'react-navigation' { declare export function withNavigation( Component: React$ComponentType ): React$ComponentType; + declare export function withNavigationFocus( + Component: React$ComponentType + ): React$ComponentType; } diff --git a/src/react-navigation.js b/src/react-navigation.js index 1e9e00ba..c2c3e1b0 100644 --- a/src/react-navigation.js +++ b/src/react-navigation.js @@ -88,4 +88,7 @@ module.exports = { get withNavigation() { return require('./views/withNavigation').default; }, + get withNavigationFocus() { + return require('./views/withNavigationFocus').default; + }, }; diff --git a/src/react-navigation.web.js b/src/react-navigation.web.js index 2516b832..ad071511 100644 --- a/src/react-navigation.web.js +++ b/src/react-navigation.web.js @@ -32,4 +32,7 @@ module.exports = { get withNavigation() { return require('./views/withNavigation').default; }, + get withNavigationFocus() { + return require('./views/withNavigationFocus').default; + }, }; diff --git a/src/views/withNavigationFocus.js b/src/views/withNavigationFocus.js new file mode 100644 index 00000000..59217627 --- /dev/null +++ b/src/views/withNavigationFocus.js @@ -0,0 +1,56 @@ +import React from 'react'; +import propTypes from 'prop-types'; +import hoistStatics from 'hoist-non-react-statics'; +import invariant from '../utils/invariant'; + +export default function withNavigationFocus(Component) { + class ComponentWithNavigationFocus extends React.Component { + static displayName = `withNavigationFocus(${Component.displayName || + Component.name})`; + + static contextTypes = { + navigation: propTypes.object.isRequired, + }; + + state = { + isFocused: false, + }; + + componentDidMount() { + const navigation = this.getNavigation(); + this.subscriptions = [ + navigation.addListener('didFocus', () => + this.setState({ isFocused: true }) + ), + navigation.addListener('willBlur', () => + this.setState({ isFocused: false }) + ), + ]; + } + + componentWillUnmount() { + this.subscriptions.forEach(sub => sub.remove()); + } + + getNavigation = () => { + const navigation = this.props.navigation || this.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() { + return ( + + ); + } + } + + return hoistStatics(ComponentWithNavigationFocus, Component); +}