React Contextを使う

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

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

## Contextを作成する
``` 
const SettingContext = React.createContext({ xxx, yyy });
```
コンポーネントの外に定義しておきます。
引数はデフォルト値になります。
親をたどってProviderがなかった場合に適用されるようです。（そんな事あるのか？）

## 値をセットする
``` 
        <SettingContext.Provider value={ xxxx }>
          <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を使うよりシンプルというのは言えるかな。
初心者の私にはわかりませんが。

https://ja.reactjs.org/docs/context.html#reactcreatecontext