LoginSignup
6
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-06-25

AppRegistry

Applicationを登録する機能

Reading

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

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

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

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

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で登録されるようです。

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
(場所見つけるのが大変・・・)

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

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に戻ります。

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

appKeyにfuncを登録するだけ。

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

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

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

登録しているrunnable functionを実行する。
appKeyがReact.Componentに対応していればappParametersinitialPropsrootTagが必須。

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

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

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

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あたりを見ていこうと思います。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2