LoginSignup
75
67

More than 5 years have passed since last update.

React の Context を使って Flux を実装する

Posted at

何の目的で使うか

ある親の要素以下では、子供はある特定の親(おそらくは根)に依存した特定の this.context の状態を持てる機能がContextである。

Reactでは要素の受け渡しにひたすら子へリレーする方法がメジャーだが、mixinで必要なプロパティはユーザーからは隠したい事が多い。そこでcontextを使う。

注意点として、これはReactのフレームワーク実装者やmixin提供者に必要な知識であって、一般的なReactアプリケーションの中でContextを使うのは複雑すぎるので個人的には推奨しない。Reactとしてもundocumetedである。

参考: React.js: Communication between Components with ContextsJScrambler Blog

概要

  1. 親で static childContextTypes = {...} と getChildContext() を実装する
  2. 子で static contextTypes = {...} を実装する
  3. 子で 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の関係を別に記述して開発すれば良い。

75
67
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
75
67