rxjs 用の suspense ライブラリを作った
Observable から suspense でデータを取得するライブラリ作った
firestore や websocket など変化したデータを watch することができる際に有効になりそうなライブラリです。
- npm https://www.npmjs.com/package/@naporin0624/react-flowder
- github https://github.com/naporin0624/react-flowder/tree/main/packages/react-flowder
- examples https://packages-sigma.vercel.app/
なにができるか
rxjs で作られる Observable を subscribe すると流れてくる1つ目の data を suspense で取得し、その後も流れてくるデータも反映してくれるものです。 ライブラリが提供している datasouce という関数は observable を作る関数を引数に取るので filter 条件や、id、画像の small, medium, large などを後刺しできます。
firestore v9 でこのライブラリを使用する際は次のようになります。
import { Observable } from "rxjs"; import { getFirestore, collection, onSnapshot } from "firebase/firestore"; import { datasource, useDataRead } from "@naporin0624/react-flowder"; type Post = { id: string; content: string; createdAt: number; } type Filter = { // filter condition params } const db = getFirestore(); const postsCollection = collection(db(), "posts"); const posts = (filter: Filter) => new Observable<Post[]>((subscriber) => { // filter params を使って query の変更などを行う return onSnapshot(ref, (snapshot) => { const posts = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() })); subscriber.next(posts); }, (err) => subscriber.error(err), () => subscriber.complete(), ); const postsDatasource = datasource((filter: Filter) => posts(filter)); const App = () => { const filter = useMemo<Filter>(() => ({ }), []); const data = useDataRead(postsDatasource(filter)); return <p>{JSON.stringify(data, null, 2)}</p> }
提供してる関数
datasource
Observable を生成する関数を引数にとって datasource として登録する関数です。 内部でキャッシュと対応付けるための unique な string の key を生成します。
Provider
内部的に持っているキャッシュの保持を行います。 このライブラリは僕が作っているライブラリを2つ掛け合わせて構成されているので、キャッシュの同期機構が Provider には書かれています。
useReadData
useReadData(datasource())
という形で使われ、データの Suspense による取得とデータの購読を行います。
useReset
useReset()
or useReset(datasource)
, useReset(datasource(xxx))
という形で使われます。
useReset は関数を返し、 その関数を実行するとキャッシュが破棄され再度 Suspense されます。
引数に datasource を入れた時に破棄されるキャッシュは datasource を引数にとって得られたものだけに限定されます。
usePrefetch
useReadData を使用せずともデータを取得してキャッシュに入れたいときに使用します。loading を state として持ちたいときに便利です。
startTransition が来るまではこれで代用できるはず。
const prefetch = usePrefetch(datasource); const onClick = async () => { await prefetch(args); }
最後に
rxjs と react が非常に親和性が高いと思い込んだ結果できたライブラリです。半年くらい使用してみた結果かなり使えることが分かったのでブログを書いてみました。特に firestore との親和性が高いです。Suspense の欠点であるデータの取得が直列になってしまい、初回のレンダリングに時間がかかるという部分に関してはまだ対応できてないですが、そこにも手を入れたいと思っています。useReadDataAll
的な hook ができるはず。ぜひ、rxjs で Suspense を体験してみたいという方に使ってみてほしいです。よろしくお願いします。
内部実装では useEffect, useState を使って記述していた部分を use-sync-external-store に置き換えてみました。
自作ライブラリを use-sync-external-store を使って置き換えました。https://t.co/4XpvpYkDEF
— naporitan (@naporin24690) 2022年3月22日
置き換える前のコードhttps://t.co/3loQdonaTb