LoginSignup
0
1

More than 5 years have passed since last update.

Render Propsからコールバックを呼ぶ

Posted at

普通はそんなことをしないと思うのですが、Render Propsからコールバックを呼ぶような処理をすることとなり、一工夫が必要となりました。

TL; DR

  • ReactはRender PhaseとCommit Phaseに分かれている
  • Render PropsはRender Phaseだけど、setStateはCommit Phaseから呼ばないといけない
  • 下層にコンポーネントをセットして、そこのイベントを介する

前提条件

もともとReact外を経由するような方法で値を取得して、onGetコールバックを駆動するようなコンポーネントを作っていました。で、React 16.3で加わったContextを使えばスッキリ書き直せるようにも思えてきたのですが、とりあえず従来のコンポーネントも(上に<Provider>をセットすることを除けば)そのまま使えるようなものに置き換えておこうと考えました。

Render Propsとは

詳しくはReactのドキュメント別の方の記事に譲りますが、Render Propsは、「propとして、コンポーネントを返す関数を取る」技法です。この関数の引数を外側のコンポーネントでセットすることで、関数の出力となるコンポーネントの挙動を変化させることができます。

なお、propの名前がrenderとは限りません。実際、Context.Consumerも子要素 =childrenに関数を取ります。

素直に書くとエラーに

まずは、素直に書いてみました。

function ContextGetter(props){
  return(
    <Context.Consumer>
      { 
        value => {
          this.props.onGet(value);
          return null;
        } 
      }
    </Context.Consumer>
  );
}

このようにしたら動く…と思いきや、「renderの中からsetStateは呼べません」のようなエラーとなってしまいました。

2つのフェーズ

Reactの実行は、2つのフェーズに分かれています(ここの模式図が見やすいです)。

  • Render Phase…描画を実行する部分。React 17で非同期化が計画されていることもあり、setStateなど副作用のある操作は行えない。
  • Commit Phase…描画が完了した後処理の部分。setStateなどの実行も可能。

ということで、文字通りRender PhaseにあたるRender Propsでは、setStateを行うようなコールバックの実行はできないのでした。

では、どうする?

コールバックを呼ぶのはCommit Phaseでないとうまくいかないので、Consumerの内側にさらにコンポーネントを入れて、内側のcomponentDidMountcomponentDidUpdateなどで拾うことにしました。

function ContextGetter(props){
  return(
    <Context.Consumer>
      { 
        value => <ContextGetterInner value={value} onGet={this.props.onGet} />
      }
    </Context.Consumer>
  );
}

class ContextGetterInner extends React.PureComponent{
  componentDidMount(){
    this.props.onGet(this.props.value);
  }

  componentDidUpdate(){
    this.props.onGet(this.props.value);
  }
}

結論

素直にContextを使うほうが、ずっとわかりやすいや。

0
1
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
0
1