Open16

Tanstack Virtualでスクロール時に長時間のブロッキングが発生する症状について

ピン留めされたアイテム
ken7253ken7253

前提条件

CPUの実行速度が遅い端末などで、スクロール時の再レンダリングが非常に遅い現象があった。
確認環境としては ChromeのDevtoolsでCPUスロットリング x20 で実行した際に再描画に1s以上かかってしまうという感じ。

ken7253ken7253

ワークアラウンド

const virtualizer = useVirtualizer({
    count: 200,
    getScrollElement: () => ref.current,
    estimateSize: () => 120,
    overscan: 7,
    // ここのコールバックの第二引数に false を渡すとOK
    observeElementOffset: (_, cb) => cb(0, false),
});
ken7253ken7253

これだと、スクロール時に更新が行われなくなってしまったので直接パッチを適用して検証してみる。

@tanstack/react-virtual/dist/esm/index.js
     ...options,
     onChange: (instance2, sync) => {
       var _a;
-      if (sync) {
-        flushSync(rerender);
-      } else {
+      // if (sync) {
+      //   flushSync(rerender);
+      // } else {
         rerender();
-      }
+      // }
       (_a = options.onChange) == null ? void 0 : _a.call(options, instance2, sync);
     }
   };
ken7253ken7253

onChangesyncはどこから渡されるのか

ken7253ken7253

更にVirtualizer.notify()Virtualizer.maybeNotify()から呼ばれている。

https://github.com/TanStack/virtual/blob/15e4d94cf0fd312fb20baac04fcd9ea5bbf16512/packages/virtual-core/src/index.ts#L428-L450

ここのVirtualizer.maybeNotify()ではmemoという関数が呼ばれているがこれはreactのメモ化ではなく独自に定義したメモ化関数っぽい。

memo()の定義はここ。

https://github.com/TanStack/virtual/blob/15e4d94cf0fd312fb20baac04fcd9ea5bbf16512/packages/virtual-core/src/utils.ts#L5-L69