From fb7fe2d4e8783232c73fc48b29ad63400a8b3420 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Sun, 6 Nov 2016 20:49:10 -0800 Subject: [PATCH] modernize AppContainer and add rootTag in the child context Summary: This does 2 things: - modernize the component to use ES6 + flow - assign `rootTag` to the child context Each view in RN has its own `reactTag`. The reactTag for a root view is called `rootTag`. When there are multiple react root views active within the app (e.g. in a hybrid environment), rootTag is the only reliable "label" to differentiate them. This is especially useful when we want to limit an event/activity on a particular root view, instead of affecting all active root views. This allows components to do: ``` class Foo extends React.Component { static contextTypes = { rootTag: React.PropTypes.number, }; componentDidMount() { // Get the root tag of this component, which is static for all components under the same root view console.log(this.context.rootTag); } } ``` In a pure JS RN app environment, there will always be exactly 1 root view, so `rootTag` may usually be ignored. Reviewed By: yungsters Differential Revision: D4130376 fbshipit-source-id: 559b67615f487bad754b5832ad4a02bcef05be2a --- Libraries/ReactNative/AppContainer.js | 110 +++++++++++++-------- Libraries/ReactNative/renderApplication.js | 2 +- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/Libraries/ReactNative/AppContainer.js b/Libraries/ReactNative/AppContainer.js index a8f6f6d1f..8b3a76b8c 100644 --- a/Libraries/ReactNative/AppContainer.js +++ b/Libraries/ReactNative/AppContainer.js @@ -7,62 +7,94 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule AppContainer - * @noflow + * @flow */ 'use strict'; -var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); -var React = require('React'); -var ReactNative = require('ReactNative'); -var StyleSheet = require('StyleSheet'); -var Subscribable = require('Subscribable'); -var View = require('View'); +const EmitterSubscription = require('EmitterSubscription'); +const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); +const React = require('React'); +const ReactNative = require('ReactNative'); +const StyleSheet = require('StyleSheet'); +const View = require('View'); -var Inspector = __DEV__ ? require('Inspector') : null; -var YellowBox = __DEV__ ? require('YellowBox') : null; +// TODO (fkg): make rootTag required +type Context = { + rootTag: ?number, +}; +type Props = { + children?: React.Children, + rootTag?: number, +}; +type State = { + inspector: ?React.Element<*>, + mainKey: number, +}; -var AppContainer = React.createClass({ - mixins: [Subscribable.Mixin], +class AppContainer extends React.Component { + props: Props; + state: State = { + inspector: null, + mainKey: 1, + }; + _mainRef: ?React.Element<*>; + _subscription: ?EmitterSubscription = null; - getInitialState: function() { - return { inspector: null, mainKey: 1 }; - }, + static childContextTypes = { + rootTag: React.PropTypes.number, + }; - toggleElementInspector: function() { - var inspector = !__DEV__ || this.state.inspector - ? null - : { - this.setState( - (s) => ({mainKey: s.mainKey + 1}), - () => updateInspectedViewTag(ReactNative.findNodeHandle(this.refs.main)) - ); - }} - />; - this.setState({inspector}); - }, + getChildContext(): Context { + return { + rootTag: this.props.rootTag, + }; + } - componentDidMount: function() { - this.addListenerOn( - RCTDeviceEventEmitter, - 'toggleElementInspector', - this.toggleElementInspector - ); - }, + componentDidMount(): void { + if (__DEV__) { + this._subscription = RCTDeviceEventEmitter.addListener( + 'toggleElementInspector', + () => { + const Inspector = require('Inspector'); + const inspector = this.state.inspector + ? null + : { + this.setState( + (s) => ({mainKey: s.mainKey + 1}), + () => updateInspectedViewTag( + ReactNative.findNodeHandle(this._mainRef) + ) + ); + }} + />; + this.setState({inspector}); + }, + ); + } + } - render: function() { + componentWillUnmount(): void { + if (this._subscription) { + this._subscription.remove(); + } + } + + render(): React.Element<*> { let yellowBox = null; if (__DEV__) { + const YellowBox = require('YellowBox'); yellowBox = ; } + return ( + style={styles.appContainer} ref={(ref) => {this._mainRef = ref;}}> {this.props.children} {yellowBox} @@ -70,9 +102,9 @@ var AppContainer = React.createClass({ ); } -}); +} -var styles = StyleSheet.create({ +const styles = StyleSheet.create({ appContainer: { flex: 1, }, diff --git a/Libraries/ReactNative/renderApplication.js b/Libraries/ReactNative/renderApplication.js index 3fa622f85..29610faea 100644 --- a/Libraries/ReactNative/renderApplication.js +++ b/Libraries/ReactNative/renderApplication.js @@ -31,7 +31,7 @@ function renderApplication( 'Expect to have a valid rootTag, instead got ', rootTag ); ReactNative.render( - +