Permalink
Browse files

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
  • Loading branch information...
1 parent 6c04b35 commit fb7fe2d4e8783232c73fc48b29ad63400a8b3420 @fkgozali fkgozali committed with Facebook Github Bot Nov 7, 2016
Showing with 72 additions and 40 deletions.
  1. +71 −39 Libraries/ReactNative/AppContainer.js
  2. +1 −1 Libraries/ReactNative/renderApplication.js
@@ -7,72 +7,104 @@
* 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
- : <Inspector
- inspectedViewTag={ReactNative.findNodeHandle(this.refs.main)}
- onRequestRerenderApp={(updateInspectedViewTag) => {
- 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
+ : <Inspector
+ inspectedViewTag={ReactNative.findNodeHandle(this._mainRef)}
+ onRequestRerenderApp={(updateInspectedViewTag) => {
+ this.setState(
+ (s) => ({mainKey: s.mainKey + 1}),
+ () => updateInspectedViewTag(
+ ReactNative.findNodeHandle(this._mainRef)
+ )
+ );
+ }}
+ />;
+ this.setState({inspector});
+ },
+ );
+ }
+ }
+
+ componentWillUnmount(): void {
+ if (this._subscription) {
+ this._subscription.remove();
+ }
+ }
- render: function() {
+ render(): React.Element<*> {
let yellowBox = null;
if (__DEV__) {
+ const YellowBox = require('YellowBox');
yellowBox = <YellowBox />;
}
+
return (
<View style={styles.appContainer}>
<View
collapsable={!this.state.inspector}
key={this.state.mainKey}
- style={styles.appContainer} ref="main">
+ style={styles.appContainer} ref={(ref) => {this._mainRef = ref;}}>
{this.props.children}
</View>
{yellowBox}
{this.state.inspector}
</View>
);
}
-});
+}
-var styles = StyleSheet.create({
+const styles = StyleSheet.create({
appContainer: {
flex: 1,
},
@@ -31,7 +31,7 @@ function renderApplication<Props>(
'Expect to have a valid rootTag, instead got ', rootTag
);
ReactNative.render(
- <AppContainer>
+ <AppContainer rootTag={rootTag}>
<RootComponent
{...initialProps}
rootTag={rootTag}

0 comments on commit fb7fe2d

Please sign in to comment.