Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Performance of rendering over 10k items using React

Performance of rendering over 10k items using React

9eed44f137609e6ce3b6f1e14f80b9e1?s=128

Masayuki Izumi

May 10, 2017
Tweet

More Decks by Masayuki Izumi

Other Decks in Programming

Transcript

  1. Performance of rendering over 10k items using React React反省会 @

    Wantedly by @izumin5210
  2. @izumin5210 (Masayuki Izumi)

  3. Wantedly People for PC の,話をします

  4. ### 技術stack ‑ React + ReduxのSPA ‑ generatorとasync/await使い放題 ‑ ドメインロジックはredux‑sagaからservice呼ぶ

    ‑ Flow + ESLint ‑ テストはドメインロジックがほとんど・E2Eはなし ‑ css‑module, PostCSS
  5. ### dependencies ‑ `immutable` 3.8.1 ‑ `react` 15.5.3 ‑ `react-router`

    4.0.0 ‑ `redux` 13.6.0 ‑ `redux-saga` 0.14.3 ‑ `reselect` 2.5.4
  6. ### 開発体制 ‑ 1月末スタート ‑ 4/10リリース ‑ エンジニアひとり(週2.5くらい) ‑ APIとモバイルアプリのコード読みながら開発

    ‑ リリース1週間前に増援
  7. 反省

  8. None
  9. 重い

  10. やせたい

  11. 目標 つながりが20000人以上いても 快適に利用できるように

  12. renderingされる回数を減らす

  13. None
  14. ### `shouldComponentUpdate` ‑ 検索・選択など,リストやアイテムの更新回数が増えがち ‑ 文字入力するたびにリストアイテム全件updateしていた

  15. #### 対策前

  16. #### `shouldComponentUpdate` 適用後

  17. ### ユーザ入力のdebounce ‑ 「1文字入力するごとに再描画」はヤバい ReactiveX ‑ Debounce operator

  18. #### debounceの実装 ‑ `lodash.debounce` でコールバックの呼び出しを減らす ‑ `redux-saga` でも実装できる(後述) ‑ ※

    throttleだとユーザの入力を取りこぼすので注意
  19. #### 参考: redux‑sagaによるdebounceの実装 ``` function* handleInput({ input }) { yield

    call(delay, 500) // debounce by 500ms // ... } function* watchInput() { yield takeLatest('INPUT_CHANGED', handleInput); } ``` https://redux‑saga.js.org/docs/recipes/
  20. `immutable` まわりのチューニング

  21. ### `immutable` ‑ `immutable` なデータ構造を提供 ‑ ↓ こういうのができて便利 ``` Object.assign({},

    post, {title: "Title") {...post, title: 'Title'} post.set('title', 'Title') ``` React使い必見! Immutable.jsでReactはもっと良くなる | Wantedly Engineer Blog
  22. #### `shouldComponentUpdate` と `immutable` ↓ こんなふうにしてた ``` import { is

    } from 'immutable' shouldComponentUpdate({ user }: Props) { return is(user, this.props.user) } ```
  23. Componentの更新が遅い問題

  24. 対策

  25. ### `Immutable.is()` は重い ‑ `immutable` は変更なければ同一インスタンスが返るの で,ふつうの比較演算子でOK ‑ コレクションに `is()`

    使うとヤバい ‑
  26. ### `React.PureComponent` ‑ 勝手に `shouldComponentUpdate` 定義してくれる君 ‑ `props` と `state`

    の値をそれぞれ `shallowEqual` ‑ `React.Component` → `React.PureComponent` だけで充分 な場合も多い
  27. ### その他 `immutable` の注意点 ‑ `toJS()` も当然重い(recursiveなので) ‑ `Map#get()` と

    `Map#has()` はO(1)ではない ‑ ↑ `map` や `filter` が普通より重くなりやすい
  28. Componentおおすぎ問題

  29. ### 画面外のものは描画しない ‑ 100個を超えるcomponentがすべて描画されてた ‑ 画面外のものを描画するのは無駄

  30. ### `react-lazyload` `LazyLoad` 内は画面内に入るまで描画されない ``` <LazyLoad height={80}> <ContactItem {...{user}} />

    </LazyLoad> ```
  31. #### スクロール監視してるやつ多すぎ問題 ‑ Componentが1000を超えるとしんどい ‑ `<LazyLoad>` がばらばらにスクロール監視をしていた

  32. ### `react-virtualized` ‑ スクロールを監視するのはリストの親のみ ``` <List {...{ width, height, rowCount,

    rowHeight }} rowRenderer={({ key, index: i, style }) => ( <ContactItem {...{ key, style, user: users.get(i) }} /> )} /> ```
  33. ## Conclusion ‑ `shouldComponentUpdate` or `PureComponent` を使う ‑ 量の多いイベントは `debounce`

    や `throttle` を検討 ‑ `immutable` の `is()` や `toJS()` は重い ‑ `immutable` の `get()` は `has()` は `O(1)` ではない ‑ 画面外のComponentは描画しない ‑ 巨大なリストには `react-virtualized` が有効