概要
あるコンポーネントから、子、孫のコンポーネントに値を渡すのに 通常だと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を使うよりシンプルというのは言えるかな。