コンポーネント間で情報の受け渡し - React入門

コンポーネント間で情報の受け渡し – React入門

React入門の第10回は、Reactのコンポーネント間の情報の受け渡しについて解説を行います。

親コンポーネントから子コンポーネントへ

親コンポーネントから子コンポーネントへ情報を引き渡す方法には、これまで解説してきたpropsを利用します。

以下は親コンポーネント内で子コンポーネントを、子コンポーネント内で孫コンポーネントを描画するサンプルです。

親コンポーネントに関しては「myProp=’まいぷろっぷ’」と具体的なpropsを引き渡していますが、以降は「myProp={this.props.myProp}」として親から引き継いだpropsを子へ、孫へ伝えているコードとなります。

//孫コンポーネント
class GrandChildComponent extends React.Component {
  render() {
    return (
      <div>
        <p>GrandChildComopnent:{this.props.myProp}</p>
      </div>
    )
  }
}
//子コンポーネント
class ChildComponent extends React.Component {
  render() {
    return (
      <div>
        <p>ChildComopnent:{this.props.myProp}</p>
        <GrandChildComopnent myProp={this.props.myProp} />
      </div>
    )
  }
}
//親コンポーネント
class ParentComponent extends React.Component {
  render() {
    return (
      <div>
        <p>ParentComopnent:{this.props.myProp}</p>
        <ChildComopnent myProp={this.props.myProp} />
      </div>
    )
  }
}
//コンポーネントの描画
ReactDOM.render(
  <ParentComponent myProp='まいぷろっぷ' />,
  document.getElementById('app')
);

こういった、子コンポーネントに情報を引き渡す処理処理はバケツリレーとよばれており、コンポーネントが深くなれば深くなるほど、記述量が増えソースコードを複雑にしていきます。

この手間はJSXのスプレッド演算子を利用しオブジェクトを展開して設定することである程度軽減することができます。

//孫コンポーネント
class GrandChildComopnent extends React.Component {
  render() {
    return (
      <div>
        <p>GrandChildComopnent:{this.props.myProp1}/{this.props.myProp2}</p>
      </div>
    )
  }
}
//子コンポーネント
class ChildComopnent extends React.Component {
  render() {
    return (
      <div>
        <p>Childomopnent:{this.props.myProp1}/{this.props.myProp2}</p>
        <GrandChildComopnent {...this.props} />
      </div>
    )
  }
}
//親コンポーネント
class ParentComopnent extends React.Component {
  render() {
    return (
      <div>
        <p>ParentComopnent:{this.props.myProp1}/{this.props.myProp2}</p>
        <ChildComopnent {...this.props} />
      </div>
    )
  }
}
var props={
  myProp1:'まいぷろっぷ1',
  myProp2:'まいぷろっぷ2'
}
//コンポーネントの描画
ReactDOM.render(
  <ParentComopnent {...props}/>,
  document.getElementById('app')
);

子コンポーネントから親コンポーネントへ

次に子コンポーネントから親コンポーネントへ情報を伝達する方法です。

こちらは子コンポーネントが生成されたタイミングで親コンポーネントは生成されているので、子コンポーネントが親コンポーネントのステートを変更することで実現します。

以下のサンプルでは、次のような流れで孫コンポーネントから子コンポーネントのステートを変更しています。

  1. 孫コンポーネントのステートに引き渡す情報のmyState1とmyState2を定義
  2. 子コンポーネント内で自身のステートを変更するupdateState()として定義
  3. 子コンポーネントのupdateState()はpropsとして孫コンポーネントに引き渡す
  4. 孫コンポーネントで子コンポーネントより引き継いだupdateState()を実行することで子コンポーネントのステートを変更

同様の流れで親コンポーネントのステートを子コンポーネントから変更しています。

//孫コンポーネント
class GrandChildComopnent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      myState1 : 'まいすてーと1',
      myState2 : 'まいすてーと2'
    };
    //子コンポーネントのステートを更新
    this.props.updateState(this.state);
  }
  render() {
    return (
      <div>
        <p>GrandChildComopnent:{this.state.myState1}/{this.state.myState2}</p>
      </div>
    )
  }
}
//子コンポーネント
class ChildComopnent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      myState1 : null,
      myState2 : null
    };
  }
  updateState(state){
    this.setState(state);
    //親コンポーネントを更新
    this.props.updateState(state);
  }
  render() {
    return (
      <div>
        <p>ChildComopnent:{this.state.myState1}/{this.state.myState2}</p>
        <GrandChildComopnent updateState={this.updateState.bind(this)} />
      </div>
    )
  }
}
//親コンポーネント
class ParentComopnent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      myState1 : null,
      myState2 : null
    };
  }
  updateState(state){
    this.setState(state);
  }
  render() {
    return (
      <div>
        <p>ParentComopnent:{this.state.myState1}/{this.state.myState2}</p>
        <ChildComopnent updateState={this.updateState.bind(this)}  />
      </div>
    )
  }
}
//コンポーネントの描画
ReactDOM.render(
  <ParentComopnent/>,
  document.getElementById('app')
);

Ref関数

Ref関数を用いることで親から子のメソッドを実行することができ、親から子や子から親への情報伝達が可能になります。

親から子へ

コンポーネント利用時にref属性でコンポーネントに任意の名前を作成することができ、this.refs.コンポーネント名.メソッド名()で子コンポーネントのメソッドを実行することができます。実行のタイミングはReactのライフサイクルでcomponentDidMountが完了した後です。

以下のサンプルでは親コンポーネントから子コンポーネントのincrementというメソッドを実行して、子コンポーネントのステートを毎秒変更しています。

//子コンポーネント
class ChildComopnent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count : 0
    };
  }
  increment(){
    this.setState({
      count : ++this.state.count
    });
  }
  render() {
    return (
      <div>
        <p>ChildComopnent</p>
        <p>{this.state.count}</p>
      </div>
    )
  }
}
//親コンポーネント
class ParentComopnent extends React.Component {
  componentDidMount(){
    setInterval(()=>{
      this.refs.MyChildComponent.increment();
    },1000);
  }
  render() {
    return (
      <div>
        <p>ParentComopnent</p>
        <ChildComopnent ref='MyChildComponent' />
      </div>
    )
  }
}
//コンポーネントの描画
ReactDOM.render(
  <ParentComopnent />,
  document.getElementById('app')
);

子から親へ

子コンポーネント内に情報を返すメソッドを用意すれば親コンポーネントから参照して情報を取得することができます。

以下のサンプルでは子コンポーネントは自身でstate.countを毎秒加算するように設定されており、getCount()が実行された際に現在のstate.countを返します。
親コンポーネントでは「ParentComponent」がクリックされた際に子コンポーネントのstate.countを自身のstate.countに設定するようにしています。

//子コンポーネント
class ChildComopnent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count : 0
    };
    setInterval(()=>{
      this.setState({
        count : ++this.state.count
      });
    },1000);
  }
  getCount(){
    return this.state.count;
  }
  render() {
    return (
      <div>
        <p>ChildComopnent</p>
        <p>{this.state.count}</p>
      </div>
    )
  }
}
//親コンポーネント
class ParentComopnent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count : 0
    };
  }
  getCount(){
    this.setState({
      count : this.refs.MyChildComponent.getCount()
    });
  }
  render() {
    return (
      <div>
        <p onClick={this.getCount.bind(this)}>ParentComopnent</p>
        <p>{this.state.count}</p>
        <ChildComopnent ref='MyChildComponent' />
      </div>
    )
  }
}
//コンポーネントの描画
ReactDOM.render(
  <ParentComopnent />,
  document.getElementById('app')
);

今回は様々な方法で情報の受け渡しについて行いましたが、ReactはViewに特化しており正直に言って状態/情報管理はそれほど得意ではありません。複雑なアプリケーションの場合は、React単体で情報を管理しようとせずにReduxなどのFluxアーキテクチャーを利用したほうがよいでしょう。

次回はwebpackの設定方法について解説を行います。