Slide 1

Slide 1 text

Performance of rendering over 10k items using React React反省会 @ Wantedly by @izumin5210

Slide 2

Slide 2 text

@izumin5210 (Masayuki Izumi)

Slide 3

Slide 3 text

Wantedly People for PC の,話をします

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

### 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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

反省

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

重い

Slide 10

Slide 10 text

やせたい

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

renderingされる回数を減らす

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

#### 対策前

Slide 16

Slide 16 text

#### `shouldComponentUpdate` 適用後

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

`immutable` まわりのチューニング

Slide 21

Slide 21 text

### `immutable` ‑ `immutable` なデータ構造を提供 ‑ ↓ こういうのができて便利 ``` Object.assign({}, post, {title: "Title") {...post, title: 'Title'} post.set('title', 'Title') ``` React使い必見! Immutable.jsでReactはもっと良くなる | Wantedly Engineer Blog

Slide 22

Slide 22 text

#### `shouldComponentUpdate` と `immutable` ↓ こんなふうにしてた ``` import { is } from 'immutable' shouldComponentUpdate({ user }: Props) { return is(user, this.props.user) } ```

Slide 23

Slide 23 text

Componentの更新が遅い問題

Slide 24

Slide 24 text

対策

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

### `React.PureComponent` ‑ 勝手に `shouldComponentUpdate` 定義してくれる君 ‑ `props` と `state` の値をそれぞれ `shallowEqual` ‑ `React.Component` → `React.PureComponent` だけで充分 な場合も多い

Slide 27

Slide 27 text

### その他 `immutable` の注意点 ‑ `toJS()` も当然重い(recursiveなので) ‑ `Map#get()` と `Map#has()` はO(1)ではない ‑ ↑ `map` や `filter` が普通より重くなりやすい

Slide 28

Slide 28 text

Componentおおすぎ問題

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

### `react-lazyload` `LazyLoad` 内は画面内に入るまで描画されない ``` ```

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

### `react-virtualized` ‑ スクロールを監視するのはリストの親のみ ``` ( )} /> ```

Slide 33

Slide 33 text

## Conclusion ‑ `shouldComponentUpdate` or `PureComponent` を使う ‑ 量の多いイベントは `debounce` や `throttle` を検討 ‑ `immutable` の `is()` や `toJS()` は重い ‑ `immutable` の `get()` は `has()` は `O(1)` ではない ‑ 画面外のComponentは描画しない ‑ 巨大なリストには `react-virtualized` が有効