【React】 PureComponent にアロー関数を渡してはいけない

React v15.3 で追加された PureComponent は便利だけど、少し間違えるとパフォーマンス上がりませんよという話。

記事を書こうと思い立ったあとで調べたら先人がいらっしゃった。

二番煎じだが、自戒のためにもメモを残しておく。

PureComponent とは

shouldComponentUpdate をよしなにしてくれる。 props を shallow 比較して変更があったら render する。

React のレンダリングパフォーマンスを上げようと思ったら shouldComponentUpdate をいじるのは避けて通れないけど、PureComponent のおかげであまり考えなくて済む。 state を持たないコンポーネントはだいたい PureComponent 使えば良い感がある。

しかし、思わぬ落とし穴がある。

PureComponent にアロー関数を渡してはいけない

本質だけを書くと、 ButtonPureComponent だとして、次のように使うとする。

render () {
  return (
    <Button onClick={() => this.setState({clicked: true})} text='hoge' />
  )
}

このとき、この render が実行されるたびに、 onClick に渡すアロー関数が再生成される。すると、 ButtonshouldComponentUpdate が「propsに変更があったね」という判定をして true を返す。だから Button 内の render が毎回実行されるというわけだ。

shouldComponentUpdate: 「何者だ!」
onClick: 「新しい関数です」
shouldComponentUpdate: 「よし通れ」

こういう感じである。だからせっかく PureComponent でパフォーマンス向上しようとしても、上のほうのコンポーネントがアロー関数を使っていたりすると、全体の state が変わるたびに末端のコンポーネントまで render が走ったりしてしまう。思わぬ落とし穴だ。

ほかのアンチパターン

上のブログ記事にせっかく載っていたので、ほかのアンチパターンも紹介する。アンチパターンである理由は上と同じなので、繰り返さない。

コードは引用する。

デフォルト値として新たに変数を宣言しちゃうやつ。

class Table extends PureComponent {
  render() {
    return (
      <div>
        {this.props.items.map(i =>
          <Cell data={i} options={this.props.options || []} />
         )}
       </div>
     );
  }
}

bind を render の中でやっちゃうやつ。

class App extends PureComponent {
  update(e) {
    this.props.update(e.target.value);
  }
  render() {
    return <MyInput onChange={this.update.bind(this)} />;
 }
}