ReactNativeソースコードリーディング <AppRegistry>

## AppRegistry

Applicationを登録する機能

## Reading

`AppRegistry` should be `require`d early in the `require` sequence to make sure the JS execution environment is setup before other modules are `require`d.

他のReactNativeモジュールよりも前に`require`しましょうという話。
`import`もおそらくそうでしょう。

```jso:AppRegsitry.js
type AppRegistry = {
  registerConfig: Function;
  registerComponent: Function;
  registerRunnable: Function;
  getAppKeys: Function;
  runApplication: Function;
  unmountApplicationComponentAtRootTag: Function;
};
```

型は大雑把にこんな感じ。1つずつメソッドを見ていきます。

```js:registerConfig

type AppConfig = {
  appKey: string;
  component?: ComponentProvider;
  run?: Function;
};

registerConfig: function(config: Array<AppConfig>) {
  for (var i = 0; i < config.length; ++i) {
    var appConfig = config[i];
    if (appConfig.run) {
      AppRegistry.registerRunnable(appConfig.appKey, appConfig.run);
    } else {
      invariant(appConfig.component, 'No component provider passed in');
      AppRegistry.registerComponent(appConfig.appKey, appConfig.component);
    }
  }
}
```

configを渡すことで、runnnable(runをimplementしている)ならば`registerRunnable`で登録され、そうでなければ`registerComponent`で登録されるようです。

```js:registerComponent
type ComponentProvider = () => ReactClass<any>;

registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {
  runnables[appKey] = {
    run: (appParameters) =>
      renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
  };
  return appKey;
},
```

Applicationのkeyと`ComponentProvider`を渡します。
`ComponentProvider`は型の通りlambdaでReact.Componentを返すものです。
`runnables[appKey]`にfunctionを登録した後に、appKeyを返すだけですね。
ここで出て来た、`renderApplication`を見ていきましょう。
https://github.com/facebook/react-native/blob/master/Libraries/ReactIOS/renderApplication.js
(場所見つけるのが大変・・・)

```js:renderApplication.js
function renderApplication<Props>(
  RootComponent: ReactClass<Props>,
  initialProps: Props,
  rootTag: any
) {
  invariant(
    rootTag,
    'Expect to have a valid rootTag, instead got ', rootTag
  );
  ReactNative.render(
    <AppContainer>
      <RootComponent
        {...initialProps}
        rootTag={rootTag}
      />
    </AppContainer>,
    rootTag
  );
}
```

`AppContainer`のchildrenに引数で渡されるReactComponentを設定しています。
ここから先ほどの`runnables[appKey].run(appParamters)`のappParametersが`appParameters= { initialProps: { a: "a", b: "b", c: "c" }, rootTag: rt }`のようにならなければいけないことがわかります。

さて、一応`AppContainer`も見ておきましょう。
https://github.com/facebook/react-native/blob/master/Libraries/ReactIOS/AppContainer.js

```js:AppContainer.js
var AppContainer = React.createClass({
  mixins: [Subscribable.Mixin],

  getInitialState: function() {
    return { inspector: null, mainKey: 1 };
  },

  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});
  },

  componentDidMount: function() {
    this.addListenerOn(
      RCTDeviceEventEmitter,
      'toggleElementInspector',
      this.toggleElementInspector
    );
  },

  render: function() {
    let yellowBox = null;
    if (__DEV__) {
      yellowBox = <YellowBox />;
    }
    return (
      <View style={styles.appContainer}>
        <View
          collapsable={!this.state.inspector}
          key={this.state.mainKey}
          style={styles.appContainer} ref="main">
          {this.props.children}
        </View>
        {yellowBox}
        {this.state.inspector}
      </View>
    );
  }
});

var styles = StyleSheet.create({
  appContainer: {
    flex: 1,
  },
});
```

`render`を見ると、`<View>`の中にchildrenをrenderingしているだけのようです。
ES5かつmixinがまだあるときに作成されたみたいで最新のReactスタイルではないですね。
mixinのためだけにextendsするのも面倒だからこのままなのでしょうかね。
`{yellowBox}`と`inspector`は`__DEV__=true`の場合のみなのでここではスルーします。
ここまでで`renderApplication`の中身が分かったので、再び`AppRegistry`に戻ります。

```js:registerRunnable
registerRunnable: function(appKey: string, func: Function): string {
  runnables[appKey] = {run: func};
  return appKey;
}
```

appKeyにfuncを登録するだけ。


```js:getAppKeys
getAppKeys: function(): Array<string> {
  return Object.keys(runnables);
}
```

runnablesに登録されているappKeyを取得するだけ。

```js:runApplication
runApplication: function(appKey: string, appParameters: any): void {
  const msg = // Debug Log用の文字列だと思うので省略
  runnables[appKey].run(appParameters);
},
```

登録しているrunnable functionを実行する。
`appKey`がReact.Componentに対応していれば`appParameters`は`initialProps`と`rootTag`が必須。

```js:unmountApplicationComponentAtRootTag
unmountApplicationComponentAtRootTag: function(rootTag : number) {
  ReactNative.unmountComponentAtNodeAndRemoveContainer(rootTag);
},
```

ここの`ReactNative.unmountComponentAtNodeAndRemoveContainer`がいくら探しても見当たらなかったので諦めます。
名前の通りなら、`renderApplication`で登録したrunnablesを削除するのかな、と思っています。

最後に、`BatchedBridge`を見て終わります。


```js:BatchedBridge
const BatchedBridge = new MessageQueue(
  () => global.__fbBatchedBridgeConfig,
  serializeNativeParams
);

class MessageQueue {
  constructor(configProvider: () => Config, serializeNativeParams: boolean) {
    this._callableModules = {};
    this._queue = [[], [], [], 0];
    this._callbacks = [];
    this._callbackID = 0;
    this._callID = 0;
    this._lastFlush = 0;
    this._eventLoopStartTime = new Date().getTime();
    this._serializeNativeParams = serializeNativeParams;
    
    ...,
  },

  registerCallableModule(name, methods) {
    this._callableModules[name] = methods;
  }
}

BatchedBridge.registerCallableModule(
  'AppRegistry',
  AppRegistry
);
```

methodsという名前から、nameのメソッドを呼び出していく目的がありそう。

次回はViewあたりを見ていこうと思います。