PUROGU LADESU

ポエムがメインのブログです。

React Context を使う

概要

あるコンポーネントから、子、孫のコンポーネントに値を渡すのに 通常だとpropsを使って順番に橋渡ししていくので手間ですが、 Contextを使うことでそんな面倒はなしにグローバルに値にアクセスできます。 アプリ全体で使う設定をもたせるのに便利だと思います。 Reduxが嫌いな人におすすめです。

下記サンプルではParentView -> MiddleView -> ChildViewの階層になっています。

Contextを作成する

const SettingContext = React.createContext({ xxx, yyy });

コンポーネントの外に定義しておきます。 引数はデフォルト値になります。 親をたどってProviderがなかった場合に適用されるようです。(そんな事あるのか?)

値をセットする

        <SettingContext.Provider value={this.state}>
          <MiddleView setting={this.state} />
        </SettingContext.Provider>

作成したContextのProviderをタグに使ってvalueに値を入れます。

値をゲットする

やり方は2通りあるようです。

  • contextTypeを設定する クラスのcontextTypeプロパティにコンテキストを設定します。 そしてthis.contextで値を取り出します。
class ChildView1 extends React.Component {
  render() {
    return (
      <div>
        <h3>Passed by contextType</h3>
        <p>{this.context.color}</p>
        <p>{this.context.size}</p>
      </div>
    );
  }
}
ChildView1.contextType = SettingContext;
  • Consumerを使う 作成したContextのConsumerをタグに使います。 中で関数の引数に値が入るのでそれを使います。
class ChildView2 extends React.Component {
  render() {
    return (
      <div>
        <SettingContext.Consumer>
          {context => {
            return (
              <React.Fragment>
                <h3>Passed by Consumer</h3>
                <p>{context.color}</p>
                <p>{context.size}</p>
              </React.Fragment>
            );
          }}
        </SettingContext.Consumer>
      </div>
    );
  }
}

子のコンポーネントから値を更新する

メソッドを作成してProviderに渡して値と同じように使います。

  this.updateState = () => {
      console.log('update');
      this.setState({
        color: this.state.color + '!',
        size: this.state.size + 1
      });
    };

サンプルコード

import React from 'react';

// Contextを定義
const SettingContext = React.createContext({
  // default values
  color: 'red',
  size: 40,
  updateState: () => {},
  resetState: () => {}
});

// Context使わない場合
class ChildView extends React.Component {
  render() {
    return (
      <div>
        <h3>Passed by props</h3>
        <p>{this.props.setting.color}</p>
        <p>{this.props.setting.size}</p>
      </div>
    );
  }
}

// contextTypeを追加
class ChildView1 extends React.Component {
  render() {
    return (
      <div>
        <h3>Passed by contextType</h3>
        <p>{this.context.color}</p>
        <p>{this.context.size}</p>
        <button onClick={this.context.updateState}>update</button>
      </div>
    );
  }
}
ChildView1.contextType = SettingContext;

// Consumerを追加
class ChildView2 extends React.Component {
  render() {
    return (
      <div>
        <SettingContext.Consumer>
          {context => {
            return (
              <React.Fragment>
                <h3>Passed by Consumer</h3>
                <p>{context.color}</p>
                <p>{context.size}</p>
                <button onClick={context.updateState}>update</button>
              </React.Fragment>
            );
          }}
        </SettingContext.Consumer>
      </div>
    );
  }
}

class MiddleView extends React.Component {
  render() {
    return (
      <div>
        <h2>Parent</h2>
        <ChildView setting={this.props.setting} />
        <ChildView1 />
        <ChildView2 />
      </div>
    );
  }
}

class ParentView extends React.Component {
  constructor(props) {
    super(props);

    this.updateState = () => {
      console.log('update');
      this.setState({
        color: this.state.color + '!',
        size: this.state.size + 1
      });
    };

    this.state = {
      color: 'green',
      size: 50,
      updateState: this.updateState
    };
  }

  render() {
    return (
      <div className="App">
        <SettingContext.Provider value={this.state}>
          <MiddleView setting={this.state} />
        </SettingContext.Provider>
        <div>
          <h2>default</h2>
          <ChildView1 />
          <ChildView2 />
        </div>
      </div>
    );
  }
}

export default ParentView;

まとめ

contextTypeの方がスッキリして見えるがクラス外にコードを書くのが嫌。 単純なものならReduxを使うよりシンプルというのは言えるかな。

ja.reactjs.org