夢見がちWeb屋の浮かれ雑記帳

フロントエンド周りの技術ネタやらなんやらね

React.jsでPureRenderをHOCパターン化したらReduxでも捗った件

React.jsでのパフォーマンス最適化に関しては、昔はPureRenderMixinを使ってRenderを最小化しろと言われてた。 でもある時、公式ブログでやっぱMixin難しくね?って話が出てきて、shallowCompareを使えとなった。

facebook.github.io

var shallowCompare = require('react-addons-shallow-compare');

var Button = React.createClass({
  shouldComponentUpdate: function(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  },

  // ...

});

的な。

一方Gihubの星がやたら多いReduxさんは

github.com

Renderingの最適化はreact-reduxに任せておけばえーねんと宣まい、 ReactのComponentは全部Stateless Functionにしとけばいい(classにする必要はない)と言う。

const Button = (props) => { /* ... */ }

確かにReduxをちゃんと実装すればshouldComponentUpdateはほとんど気にしなくていい。

が、やっぱりちゃんとやろうと思うと必要な場面が出てくる。 繰り返し項目とかね。

react-reduxは内部的にshallowCompareを使うので、connectでbindしたプロパティがArrayの場合、 Arrayの一部だけ変化だとしてもArrayそのもののインスタンスが変われば子要素全てが再レンダリングされてしまう。

const list = (entities) => entities.map((entity) => (
   <MyListItem key={ entitye.id } entity={ entity } />
)

みたいな場合、entitiesの一部だけが変わった場合の最適化はそれぞれのMyListItem側で最適化しなければならない。

この場合、MyListItemがContainer Componentだったら同じくreact-reduxに最適化に任せれば良いのだけど、Presentational Componentだったら結局、自前でshouldComponentUpdate判定する必要がある。

medium.com

でもそのためだけにStateless Functionをやめてclass化するのはなんかやだ。

const MyListItem = (props) => { /* ... */ }

的なシンプルさを保ちたい。

どうすれば良いのか。

そこでHOC (Higher-Order Components)ですよ。

shouldComponentUpdateを個別に書くのをがめんどくさいのならwrapperで共通化しちゃえば良い。

つまり、

const asPure = (Component) => 
  class AsPure extends React.Component {
      shouldComponentUpdate (nextProps) {
        const s = this
        let { props } = s
        return !shallowEqual(props, nextProps)
      }

      render () {
        const s = this
        let { props } = s
        return React.createElement(Component, props)
      }
    }

export default asPure

的なwrapper関数を用意して、

const MyListItem = (props) => { /* ... */ }
export default asPure(MyListItem)

にすりゃいいじゃないか!

と言うことに気がついた。

しかしあれだな、書いてて思ったけど、 HOCだのPresentational Componentだの、どうしてReact周りはこんな難しい単語まみれになっちゃうのやら。。。