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
もおそらくそうでしょう。
type AppRegistry = {
registerConfig: Function;
registerComponent: Function;
registerRunnable: Function;
getAppKeys: Function;
runApplication: Function;
unmountApplicationComponentAtRootTag: Function;
};
型は大雑把にこんな感じ。1つずつメソッドを見ていきます。
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
で登録されるようです。
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
(場所見つけるのが大変・・・)
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
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: function(appKey: string, func: Function): string {
runnables[appKey] = {run: func};
return appKey;
}
appKeyにfuncを登録するだけ。
getAppKeys: function(): Array<string> {
return Object.keys(runnables);
}
runnablesに登録されているappKeyを取得するだけ。
runApplication: function(appKey: string, appParameters: any): void {
const msg = // Debug Log用の文字列だと思うので省略
runnables[appKey].run(appParameters);
},
登録しているrunnable functionを実行する。
appKey
がReact.Componentに対応していればappParameters
はinitialProps
とrootTag
が必須。
unmountApplicationComponentAtRootTag: function(rootTag : number) {
ReactNative.unmountComponentAtNodeAndRemoveContainer(rootTag);
},
ここのReactNative.unmountComponentAtNodeAndRemoveContainer
がいくら探しても見当たらなかったので諦めます。
名前の通りなら、renderApplication
で登録したrunnablesを削除するのかな、と思っています。
最後に、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あたりを見ていこうと思います。