気まぐれ更新

なんとく知っていた Render Props を理解する

なんとなく知っていたこと

  • render自体をpropsとして渡して、受け手のrender内で使う
  • HoC みたいなもの(厳密には違う)

説明

公式には

The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function

とあり「関数形式の props を使うことによってコンポーネント間で処理を共通化しようといったテクニック」になります

この説明だけだとなんともわかりづらいので 画面のローディング を render prop を用いて書いてみようと思います

class Loading extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isloading: false,
    };
  }

  componentDidMount() {
    this.setState({ isLoading: true });
    // 描画結果がわかりやすいようにsetTimeoutいれてます
    this.props.somePromise()
      .then(setTimeout(() => this.setState({ isLoading: false }), 2000));
  }

  render() {
    return this.props.render(this.state);
  }
}

const SomeComponent = () => {
  return <Loading
    somePromise={() => Promise.resolve()}
    render={(state) => (state.isLoading ? <div>loading...</div> : <div>loading done</div>)} />
};

このように Loadingコンポーネントは Promiseオブジェクトを返す非同期処理を props として受け取り、自身のisLoading の制御だけ行い、 描画部分の処理は呼び出し元に押し付けています

これによりコンポーネント間で共通の処理を使いまわすことができます

pros/cons

pros

cons

  • PureComponent を使ってrender props を再現しようとする時、書き方によってはその恩恵が受けられなくなってしまう

これだと render が毎回別の関数として判定されるため PureComponent の恩恵がなくなる

class RenderPropsComponent extends React.PureComponent {...}

class Hoge extends React.props {
    return <RenderPropsComponent 
        render={(state) => <OtherComponent data={state} />} 
    />   
}

そのため下記のようにインスタンスメソッドとして定義し、参照先を同じにすると上記の問題は回避できます。

class RenderPropsComponent extends React.PureComponent {...}

class Hoge extends React.props {
    renderOtherComponent(state) {
        return  <OtherComponent data={state} />;
    }
    
    return <RenderPropsComponent 
        render={this.renderOtherComponent} 
    />   
}

まとめ

思うこと

HoC との比較やどちらを使うべきか問題については別途調べてまとめたいと思っています ほとんどの場合 React Hooks のカスタムフックで代用可能っぽい