何の目的で使うか
ある親の要素以下では、子供はある特定の親(おそらくは根)に依存した特定の this.context の状態を持てる機能がContextである。
Reactでは要素の受け渡しにひたすら子へリレーする方法がメジャーだが、mixinで必要なプロパティはユーザーからは隠したい事が多い。そこでcontextを使う。
注意点として、これはReactのフレームワーク実装者やmixin提供者に必要な知識であって、一般的なReactアプリケーションの中でContextを使うのは複雑すぎるので個人的には推奨しない。Reactとしてもundocumetedである。
参考: React.js: Communication between Components with ContextsJScrambler Blog
概要
- 親で static childContextTypes = {...} と getChildContext() を実装する
- 子で static contextTypes = {...} を実装する
- 子で this.context は getChildContext の結果を参照できる
最小コード例
import React from 'react';
import Dom from 'react-dom/server';
class Child extends React.Component {
static get contextTypes() {
return {a: React.PropTypes.any};
}
render() {
return <span>{this.context.a}</span>;
}
}
class Parent extends React.Component {
static get childContextTypes() {
return {a: React.PropTypes.any};
}
getChildContext() {
return {a: 1};
}
render() {
return <Child/>;
}
}
const ret = Dom.renderToString(<Parent/>);
console.log(ret); // <span data-reactid=".1rrwza5woao" data-react-checksum="1038880381">1</span>
a = 1 としてちゃんと描画されている。
注意点として、子と親の contextTypes は直感に反して「型を明記するのにあると便利」ではなく「必須」である。ここがpropTypesと大きく異なる。
Flux を実装する
ある親以下の要素の以下では同一のEventEmitter への参照を持ちたい
Ardaでやってる dispatch mixin と同等の実装
// context
import React from 'react';
import Dom from 'react-dom';
import {EventEmitter} from 'events';
const SharedTypes = {emitter: React.PropTypes.any};
class Provider extends React.Component {
static get childContextTypes() {
return SharedTypes;
}
getChildContext() {
return {emitter: this.props.emitter};
}
}
class DispatchableComponent extends React.Component {
static get contextTypes() {
return SharedTypes;
}
dispatch(...args) {
return this.context.emitter.emit(...args);
}
}
// Usage
class App extends Provider {
render() {
return (<Content {...this.props.childState} />);
}
}
class Content extends DispatchableComponent {
render() {
return (
<span
onClick={() => this.dispatch('foo')}
>
{this.props.count}
</span>
);
}
}
const emitter = new EventEmitter();
function render(state) {
Dom.render(<App emitter={emitter} childState={state}/>, document.body);
}
// flux loop の Dispatcher 相当
const state = {count: 1};
emitter.on('foo', () => {
// 何か操作する
state.count += 1;
// 再描画
render(state);
});
render(state);
Emitterとdispatchの関係、dispatchの実装の中身、ReactとEmitterの関係を隠蔽できた。後は state と emitter のロジックを組み立てる実装、stateとReactElementの関係を別に記述して開発すれば良い。