Skip to content

Instantly share code, notes, and snippets.

@izumin5210
Last active May 12, 2017 09:29
Show Gist options
  • Save izumin5210/b4a61ed5003b1666dfe8ecd5baf683f1 to your computer and use it in GitHub Desktop.
Save izumin5210/b4a61ed5003b1666dfe8ecd5baf683f1 to your computer and use it in GitHub Desktop.
Performance of rendering over 10k items using React / 20170511 React反省会@wantedlyの資料
fontSize
32
<style> :host { --color-primary: rgba(255, 102, 102, 0.8); --spacing: 1.5625%; } :host > div { background-color: rgba(0, 0, 0, 0.04); padding: calc(3 * var(--spacing)); } * { //font-family: "FOT-Humming Std"; // font-family: "FOT-Skip Std"; // font-family: "FOT-Rodin ProN"; font-family: "FOT-RodinCattleya Pro"; // font-family: "FOT-Seurat ProN"; font-weight: 500; line-height: 1.8; box-sizing: border-box; margin: 0; padding: 0; color rgba(0, 0, 0, 0.8); } h1 { font-size: 130%; } h2 { font-size: 120%; } h2:before { content: '##'; } h3 { font-size: 110%; } h3:before { content: '###'; } h4 { font-size: 100%; } h4:before { content: '####'; } h2, h3, h4 { margin-bottom: calc(3 * var(--spacing)); border-bottom: 2px solid rgba(0, 0, 0, 0.24); } h2:before, h3:before, h4:before { font-size: 80%; color: rgba(0, 0, 0, 0.24); padding-right: var(--spacing); } a:link { color: rgba(0, 0, 0, 0.8); border-bottom: 2px dotted rgba(0, 0, 0, 0.24); text-decoration: none; } ul { margin-left: calc(2 * var(--spacing)); list-style: none; } li { margin-bottom: calc(2 * var(--spacing)); } li:before { content: '- '; font-size: 80%; color: rgba(0, 0, 0, 0.24); padding-right: var(--spacing); } code, pre { font-family: Ricty; } code { // background-color: rgba(0, 0, 0, 0.12); // padding: calc(0.25 * var(--spacing)) calc(0.5 * var(--spacing)); // border-radius: calc(0.5 * var(--spacing)); padding: 0 calc(1 * var(--spacing)); } code:before, code:after { color: rgba(0, 0, 0, 0.24); content: '`'; } pre { font-size: 85%; background-color: rgba(0, 0, 0, 0.04); padding: calc(2 * var(--spacing)); margin: calc(2 * var(--spacing)); } pre:before, pre:after { display: block; color: rgba(0, 0, 0, 0.24); content: '```'; } pre > code { padding: 0; } pre > code:before, pre > code:after { content: ''; } strong { font-weight: bold; } img { max-width: 100%; max-height: 100%; } blockquote { font-size: 90%; border-left: 4px solid rgba(0, 0, 0, 0.24); padding-left: calc(2 * var(--spacing)); margin: 0 calc(2 * var(--spacing)); } blockquote a { font-size: 85%; } .center { display: flex; align-items: center; justify-content: center; flex-direction: column; } .blue { background-color: #31bdd9; color: white; } .purple { background-color: #a96dd8; color: white; } .onlyHeading h2, .onlyHeading h3, .onlyHeading h4 { border: none; padding: 0; margin: 0; width: 100%; text-align: center; } .onlyHeading h2:before, .onlyHeading h3:before, .onlyHeading h4:before { content: ''; } </style> <style> .title { align-items: flex-start; background: linear-gradient(hsla(0,0%,100%,.2),hsla(0,0%,100%,.2)),linear-gradient(82deg,#31bdd9,#a96dd8); color: white; } .title h1 { border-bottom: 2px solid white; margin-bottom: calc(1.5 * var(--spacing)); padding: calc(1.5 * var(--spacing)) var(--spacing); font-weight: 100; font-size: 130%; } .title h1 strong { font-weight: 500; } .title p { padding: 0 var(--spacing); font-weight: 300; font-size: 95%; } </style>

Performance of rendering over 10k items using React

React反省会 @ Wantedly by @izumin5210

<style> .profile { justify-content: flex-end; } .profile img { border-radius: 50%; margin-bottom: var(--spacing); border: 3px solid rgba(0,0,0,.06); } .profile a:link { color: var(--color-primary); text-decoration: none; border: none; } </style>

@izumin5210 (Masayuki Izumi)

<style> .people { background-image: linear-gradient(hsla(0,0%,0%,.5),hsla(0,0%,0%,.5)), url(https://i.gyazo.com/4137bf47fb95274ddd7385b216c2d989.png); background-position: center center; background-size: contain; background-repeat: no-repeat; color: white; } </style>

Wantedly People for PC

の,話をします

技術stack

  • React + ReduxのSPA
  • generatorとasync/await使い放題
  • ドメインロジックはredux-sagaからservice呼ぶ
  • Flow + ESLint
  • テストはドメインロジックがほとんど・E2Eはなし
  • css-module, PostCSS

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

開発体制

  • 1月末スタート
  • 4/10リリース
  • エンジニアひとり(週2.5くらい)
  • APIとモバイルアプリのコード読みながら開発
  • リリース1週間前に増援

反省

https://gyazo.com/5e240d24265c81ff4138ae05804a58d7

重い

やせたい

目標

つながりが20000人以上いても

快適に利用できるように

renderingされる回数を減らす

https://gyazo.com/35504729c24c3bef89ec076f2c584c89

shouldComponentUpdate

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

https://gyazo.com/5842866f5bd7f90efc279313fcd1cb60

対策前

center https://gyazo.com/5842866f5bd7f90efc279313fcd1cb60

shouldComponentUpdate適用後

center https://gyazo.com/22576b2b5e447686a7fd3caa549e0ed9

ユーザ入力のdebounce

  • 「1文字入力するごとに再描画」はヤバい

ReactiveX - Debounce operator

debounceの実装

  • lodash.debounceでコールバックの呼び出しを減らす
  • redux-sagaでも実装できる(後述)
  • ※ throttleだとユーザの入力を取りこぼすので注意

参考: 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/

immutableまわりのチューニング

immutable

  • immutableなデータ構造を提供
  • ↓ こういうのができて便利
Object.assign({}, post, {title: "Title")
{...post, title: 'Title'}
post.set('title', 'Title')

React使い必見! Immutable.jsでReactはもっと良くなる | Wantedly Engineer Blog

shouldComponentUpdateimmutable

↓ こんなふうにしてた

import { is } from 'immutable'

shouldComponentUpdate({ user }: Props) {
  return is(user, this.props.user)
}

Componentの更新が遅い問題

https://gyazo.com/5b4bdc5edb7850abe1a522e7a57381a5

対策

https://gyazo.com/b576c81454da63087a9ae2a9639e837d

Immutable.is()は重い

  • immutableは変更なければ同一インスタンスが返るので,ふつうの比較演算子でOK
  • コレクションにis()使うとヤバい
  • https://gyazo.com/1973262f64f791c918d5c124105f0d59

React.PureComponent

  • 勝手にshouldComponentUpdate定義してくれる君
  • propsstateの値をそれぞれshallowEqual
  • React.ComponentReact.PureComponentだけで充分な場合も多い

その他immutableの注意点

  • toJS()も当然重い(recursiveなので)
  • Map#get()Map#has()はO(1)ではない
  • mapfilterが普通より重くなりやすい

Componentおおすぎ問題

画面外のものは描画しない

  • 100個を超えるcomponentがすべて描画されてた
  • 画面外のものを描画するのは無駄

https://gyazo.com/cc6dae9466005ef8e833da8787b58719

react-lazyload

LazyLoad内は画面内に入るまで描画されない

<LazyLoad height={80}>
  <ContactItem
    {...{user}}
  />
</LazyLoad>

スクロール監視してるやつ多すぎ問題

  • Componentが1000を超えるとしんどい
  • <LazyLoad>がばらばらにスクロール監視をしていた

https://gyazo.com/7537822afa256c06dc584582cf3c6639

react-virtualized

  • スクロールを監視するのはリストの親のみ

<List
  {...{ width, height, rowCount, rowHeight }}
  rowRenderer={({ key, index: i, style }) => (
    <ContactItem {...{ key, style, user: users.get(i) }} />
  )}
/>

Conclusion

  • shouldComponentUpdate or PureComponentを使う
  • 量の多いイベントはdebouncethrottleを検討
  • immutableis()toJS()は重い
  • immutableget()has()O(1)ではない
  • 画面外のComponentは描画しない
  • 巨大なリストにはreact-virtualizedが有効
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment