日記
Posted
夜に東京で予定があったので、オフィスに出社して仕事していた。
深夜にやったツイートがなんかすごい伸びてたのでミュートにした。基本的にTweetがたくさんfavられることとかないので、初めてミュートにする機能を使った。
仕事は自作したデータ構造をReactコンポーネントのレンダリングサイクルと接続するためのAPIを書いたり、説明用のドキュメントを書いていたりしていた。
自作したMutableなオブジェクトの変更に対してReactコンポーネントを接続する時に次のようなコードをよく書く。
import {useState, useEffect} from 'react'
interface Collection<T> = {
updateSnapshot(value: T): void
getSnapshot(): T
subscribe(callback: () => void): () => void
}
const useCollection = <T>(observableCollection: Collection<T>): T = (observableCollection) => {
const [state, updateState] = observableCollection.getSnapshot()
useEffect(() => {
return observableCollection.subscribe(() => {
updateState(observableCollection.getSnapshot())
})
}, [observableCollection])
return state
}
ReactコンポーネントはupdateStateが呼び出される度にuseStateが書かれているコンポーネントを再レンダリングするという特徴を利用して、自前で定義したMutableなCollectionに変更がある度に、useCollectionを呼び出したコンポーネントが再レンダリングさせる。ということを実現している。
これにselectorやmemo化を加えた概念がReact17までのStoreライブラリとReactを繋ぐhooksの実装としてメジャーだったのだが、React18から追加されるConcurrentModeと併用した時に、画面が一瞬だけ前の状態を表示してしまうなどのチラつきが発生してしまう問題を抱えている。
この問題を解決するためにReact18から useSyncExternalStore
というカスタムフックが提供されていてこれにstoreのメソッドを食わせることでこの問題を解決できる。
職場のコードはReact17を使っているので useSyncExternalStore
を使わなくても問題ないし、ReactからこのAPIがexportされていないので使えないが、将来的にReact18に移行する際に実装を変更することなく移行できるようにしたい。
そんな人向けのライブラリとして、 https://www.npmjs.com/package/use-sync-external-store が存在する。
これは古いReactバージョンで useSyncExternalStore
を使うためのshimsで、React18が動いている場合は自動でそちらにfallbackしてくれる便利ライブラリだ。
これを使って自作したデータ構造とReactを接続するコードとテストを書いていた。
自作したデータ構造の内部がES2015 Mapなことと、 useSyncExternalStore
内の更新検知がshallow equalsで比較するという実装が相性が悪く、そこらをどうにかするために、いくつかアイデアを試してベンチマークを取るということをやっていた。
夜は Twtiterのオタクたちと蟹を食べにかに道楽に行った。
かに道楽のコースはすべて蟹が入った料理が出てきて大変満足度が高かった。
また蟹酒も試してみたがめっちゃアルコール感 + 蟹の風味といった感じだった
終電で帰ってきたのだが、疲れてしまって今日はゲームをしていない。ゲームしたい