
14,589 文字

数日前、GitHubを見ていたら興味深いリポジトリを見つけてスターを付けたのですが、その後名前が変更されました。それがこのTanstack DBです。これは非常に興味深いものです。もしReact開発者でTannerのことをご存知なければ、知っておくべきでしょう。特にReact開発者にとってTanstack query(React Queryとしても知られる)は、基本的に必須の依存関係となっています。
Tanstackは他にもルーター、フォームなど多くの素晴らしいものを構築していますが、今回はDBを構築しています。というか、そのような位置づけです。Tanstack DBの役割は魅力的で、多くのことを学べると思いますが、これは私が少し懐疑的に感じるTanstackプロジェクトの1つでもあります。
ここには多くの深掘りすべきことがあります。なぜTanstack DBが存在するのか、それは元々何だったのか(Tanstack optimistic)、どのように機能するのか、実際に何をするためのものなのか、そしてなぜElectric SQLが関与しているのかについて。掘り下げることが多くありますが、Electric SQLの投資家である私ですら、これらの会社から報酬を受け取っているわけではありません。彼らから連絡を受けたわけでもありません。
これはすべて私が深く掘り下げて理解したいと思ったからです。でも誰かが費用を負担する必要があるので、今日のスポンサーからの簡単な言葉を聞いてから戻りましょう。
今日のスポンサーは、私が狂人のように聞こえてしまうような会社の一つです。その名前がPost Hogというほど馬鹿げているからではなく、製品全体が信じられないほど素晴らしいからです。
彼らは分析からセッションリプレイ実験、調査など、多くの本当に役立つ製品ツールのオールインワンスイートを提供しています。PingやほかのすべてのプロダクトをPost Hogなしにどうやって運営していたのか分かりません。今では毎日何時間もそのダッシュボードを見ています。Post Hogが私たちにとってどれほど役立っているか、そして安価であるかは信じられないほどです。
Post Hogを使用している会社の90%以上が無料で使用しています。これは月に100万の分析イベントが無料で提供されているからです。望めば、オープンソースなのでセルフホストすることもできます。また、Stripeでどのようなことがおきているかを知る唯一の方法が、ワンクリックで提供されたPost Hogテンプレートを通してだとは作り話のように聞こえるかもしれませんが、本当です。
そこでの統合は素晴らしいです。今ではどのユーザーがどこでいくらお金を使っているか、何件のメッセージを送信しているかなど、そのデータの混沌を非常に迅速にリンクすることができます。最終的に、すべてが実際に意味を持つ一つの場所にまとまっています。そして、LLM観測機能も素晴らしいです。もし複数の異なるAIモデルを持つ製品があり、それぞれのコストやユーザーの使用量などを把握しようとするなら、Post Hogのこの機能を使えば、彼らのヘルパーで呼び出しをラップするだけで、追加の設定作業なしですべてのデータを取得できます。非常に役立ちます。私はこのダッシュボードを毎日何度も確認しています。
割引コードを提供したいところですが、おそらく皆さんには無料で利用できるでしょうから持っていません。今日soy.link/posthogでチェックしてみてください。
深く掘り下げる前に、ここで何が起きているのか、なぜ気にすべきなのかについていくつかの基本的な理解や基盤を持つ必要があります。やや大胆な発言から始めましょう。Tanstack queryでの楽観的更新は、かなりひどいものです。
文法に馴染みがない場合、Tanstack queryを使用した基本的な例を示します。React Queryに慣れていれば、これは非常にシンプルで馴染みのあるものに見えるでしょう。このクエリを識別するためのクエリキーと、オプションで非同期の関数であるクエリ関数があります。通常、useクエリを使う理由は非同期だからです。
このリクエストの結果はdataに入り、isPendingは保留中かどうかを知らせます。また、isLoadingなど他の状態もあります。そしてエラーがある場合、JSONのデコードに失敗した場合などにもそれが返されます。これにより、Reactアプリで非同期データとインターフェースするために必要なすべての状態が提供されます。
クエリキーは特に素晴らしいです。このクエリを5か所に配置しても、クエリキーにより1つのキャッシュエンティティを共有できるからです。フェッチは一度だけ行われ、それが異なる場所で参照できます。これは非常に便利です。この例はフェッチ呼び出しなのでタイプセーフではありませんが、ここでTRPCのようなものを使用すれば、タイプセーフになります。TRPCを使えば、バックからフロントまでの型が分かるので、クライアントで取得しようとしているさまざまな状態と、期待するすべての型データが得られます。
では問題は何でしょうか?問題は楽観的更新の動作方法です。ミューテーション(React Queryがもう一つ提供するもの)がある場合、クエリとミューテーションがあります。ミューテーションは変更するもの、クエリは変更しないものです。ToDoリストに新しいToDoを追加するようなミューテーションでは、新しいToDoをこのエンドポイントにポストします。
そうすると、更新されたToDoのリストでクライアントを更新したいでしょう。怠惰な方法は、ToDoを持つクエリを無効化して、再フェッチが必要になるようにすることです。ここでやっているのは、クエリキーを指定してqueryclient.invalidateQueriesを呼び出すことです。そうすると、クエリキーの1つにtodoがあるクエリは、強制的に保留状態に戻り、新しいデータを取得するために再フェッチする必要があります。いいですね。
しかし、これはあまり楽観的ではありません。まず、キーが正しくない場合や、少し異なるキーで他の場所でそのデータをクエリしている場合、実際にはヒットしません。さらに重要なのは、新しいToDoで更新していないことです。無効化しています。そのため、更新された状態を取得するには、もう一つのネットワークリクエストとレスポンスが必要です。
つまり、新しいToDoの送信ボタンを押すと、2つのネットワーク呼び出し、ToDoをプッシュするネットワーク呼び出しと、更新されたToDoリストをフェッチする別のネットワーク呼び出しを待つ必要があります。そのため、グレーの画面で待つか、メッセージが消えてから再表示されるのを待つ必要があります。
楽観的更新は難しいです。では、実際に楽観的にするにはどうすればよいでしょうか。送信時にToDoを更新したい場合、新しいToDoを取得してAPIにリクエストを送信し、同時にUIをできるだけ早く更新したい場合、現在保留中のクエリをキャンセルする必要があります。これにより、これから引き起こすキャッシュ更新を上書きしないことが分かります。
previousTodosでは、クエリクライアントから正しいクエリデータをこのキーから取得する必要がありますが、これはタイプセーフではありません。キーは好きな場所で文字列配列としてバインドしているだけだからです。ここでクエリデータを手動で設定する必要があり、型定義が正しいことを祈るだけです。ここでReact Queryのタイプ安全性が崩壊します。このToDoキーと更新しているデータの間に直接のバインディングがないからです。
このキーが文字列配列を期待しているのに、誤って数値を入れてすべてを壊すかもしれません。または誤ってオブジェクトを入れてすべてを壊すかもしれません。このキーと、それがマッピングすることが期待されるデータの形状、取得するデータ、更新するデータの間に明確さがないからです。
これらの3行はすべて信じられないほどタイプセーフでなく、キーやデータの形状の期待を変更すると、すぐに問題が発生する可能性があります。これが悪化するのは、APIエンドポイントが返す形状を変更する場合です。文字列のリストではなくオブジェクトを返すようになった場合、コードベース全体に誤って楽観的に更新するランダムな変更が起こり、エラーが発生します。
チャットからいくつかの良い指摘があります。これはアプリの規模が大きくなるにつれて多くの問題が発生します。ToDoリストにフィルターを追加することを想像してみてください。その場合、楽観的更新をどう処理しますか?おそらく必要なのは、すべてのデータを持つToDoリスト更新をトリガーし、クライアント側でフィルタリングすることでしょう。
サーバー側でフィルタリングしていて、新しいToDoを送信する場合、そのToDoが持っているフィルターに合格するかどうかは分かりません。これらの問題は最悪です。誰にも望みません。私はそこにいて、それを経験し、それを嫌いました。あちこちでバインディングを強制したり、キーと値のストアのようなものでクエリをすべて手動で定義すれば、タイプ安全性を強制できるかもしれませんが、優雅にそれを実現している人を見たことがありません。
TK Dodo、React QueryやTanstack queryを維持している伝説の人物は、React Queryで適切に考える方法を解説した素晴らしいブログを持っています。楽観的更新に完全に焦点を当てた新しい投稿があります。それを読むと、頭がだんだん痛くなります。それが複雑な問題だからこそ、彼の投稿がこれほど複雑になる必要があるのを見たことがないからです。
彼が言うように、クライアント上でサーバーロジックを再作成する必要があります。楽観的UIでは、基本的にサーバーが何をするかを予測し、それを事前にクライアント上で実装しようとしています。ここでの例もタイプセーフではないのが恐ろしいです。この投稿は、物事がいつ起きて変化しているかのタイムラインを分解していて素晴らしいですが、一貫性とタイプ安全性の問題を修正するのは簡単ではありません。
問題は、4つの異なる場所でtodosを呼び出す場合、4つすべてが正確に同じ型定義を持つようにするか、他の何かに異なるキーを与える場合、この型定義が呼び出されるたびに、このキーが一緒に呼び出されるようにする必要があることです。これら2つが常に接触していない場合、すべてが崩壊します。そして、私たちがそれらを配置する以外、これら2つの間にリンクを保証するものは何もありません。
React Queryのタイプ安全性は、コンポーネントを離れるとすぐに崩壊します。これはReact Queryの動作方法の性質です。React Queryの大ファンであることは知っていますが、これらのことが別々であることで、型定義にずれが生じることを強調する必要があります。型が必ずリンクされるという保証はなく、ある時点でリンクが外れることが確実です。
その解決策はReact Query Kitのようなもので、create queryの呼び出しを通じてすべてのクエリ型をインスタンス化することを強制します。これでuse postを使用し、このタイプを持つものでinvalidateを呼び出すことでそれを無効化できます。これはTRPCがReact Queryでも行っている方法ですが、これが必要な解決策です。この問題を修正するには、React Queryの上に完全なレイヤーとパッケージを構築する必要があります。
楽観的更新とTanstack queryが良くないことが確立されたと思います。皆さんも同意してくれることを願っています。Tenerたちが私のこの発言に怒らないことを願っています。楽観的更新とTanstack queryは最悪です。バックエンドから恐ろしいことをすべてシミュレートしてUIを期待通りに動作させるデータモデルと、キーが好きなようにバインドされる奇妙な性質が組み合わさっています。楽観的更新にTanstack queryがあまり適していない理由はたくさんあります。
この点が確立されたので、ここでの2つの部分について話す必要があります。Tanstack optimistic(別名Tanstack DB)とElectric SQLです。まずElectricから始めます。そうすることでここでの文脈を理解するのに役立つと思います。
再度、私はElectricの投資家であることを明かしておきます。あまり多くは話しませんが、面白いことに多くの点で意見が合いません。ここでの彼らのミートアップに行くこともありますが、Electric SQLは興味深い製品です。彼らの目標は、Postgress上に同期レイヤーを構築することです。DBにデータがあり、特定のユーザーまたはクライアントに関連する部分を複製し、それを関連するクライアントに配布します。そのため、クエリやミューテーションを行うとき、より重要なのは、DBに変更を加えると、その変更が送信され、異なるクライアント間で複製されることです。
Electricの目標は、任意のPostgress databaseと接続したい任意のJavaScriptベースのクライアントで動作することです。しかし、それは彼らが考慮しなければならない多くの部分があることを意味します。認証について考える必要があり、これは非常に難しいです。なぜなら、データベース内のどの行にユーザーがアクセスできるかを知る必要があるからです。そのため、他の人のデータを同期させることを強制できません。そのため、データベースレベルで認証を定義する必要があります。
これらのことを行うには、基本的に行セキュリティが必要です。また、クライアント側と、同期されたデータをどのように統合するか、データをどのように変更するか、データをどのように楽観的に更新するか、そしてすべてをクライアント上でどのように行うかを処理する必要があります。特にReact Queryのようなものとうまく統合しようとする場合、これを正しく行うのは非常に難しいです。
私はこれを知っています。なぜなら、私は歴史的にいくつかの異なるアプローチを見てきたからです。TRPCがどのように行い、最近それをどのように変更したかを見ました。そして、Convexがどのように行ったかを見ました。彼らは特に楽観的レイヤーがそれに適していなかったため、React Queryを使用しないことに決めました。
T3 chatのコードベースを見てみましょう。最近Convexに移行し始めました。この動画が公開される頃には正式にリリースされていることを願っています。金曜日までに正式にリリースする予定です。ベータ版を試したい場合は、設定でオプトインできます。
ここにスレッドタイトルの更新ミューテーションがあります。これはユーザーがスレッドのタイトルを更新するときのものです。APIスレッド更新呼び出しがあり、これはここに存在するバックエンド関数を参照するタイプセーフな呼び出しです。これが呼び出されると、ローカルストアにアクセスできます。これは私のconvexストレージで、引数はここで渡された引数です。
このクエリ識別子とこの値でlocal storage.getクエリを呼び出すと、何が返ってくるかの型が分かります。それはthreadWithParentか未定義です。これは素晴らしいです。なぜなら、変更を加える場合、どの型を尊重する必要があるかが分かるからです。存在する場合、つまり未定義でない場合、このスレッドの配列で現在のスレッドを見つけます。
次に、タイトルが更新されたスレッドを返し、それを新しいオブジェクトに設定します。これは私が望むよりもまだ多くのステップがありますが、React queryでの同等のものよりもはるかに優れています。これらの各々が型契約の違反である代わりに、これらの各々は型と完全に一致しているからです。私はサーバーサイドのコードを自分で複製していますが、それはサイドバーにすべてのものをリストしているクエリを取得し、変更したものを更新し、新しい状態を設定しているからです。
技術的には、はい、サーバーサイドのロジックを複製していますが、もしサーバーサイドでフィルタリングしていたら、例えばスレッドを検索していて、そのタイトルの1つを更新した場合、これには影響しません。従来のサイドバークエリではなく検索を通じて表示されるスレッドの楽観的更新は得られません。まだはるかに良いですが、結局のところ、これはバックエンドがあなたに更新を送信するのを待つよりも早くユーザーに変更を見せるために、バックエンドロジックをフロントエンドで複製しています。
これはElectricによってどのように解決されていますか?実は、Electricのプラットフォームと製品はこれを直接解決していません。Electricの中核となるもの、つまりGitHubにあるものを見ると、ここで彼らが構築したのは、面白いことに私のお気に入りの言語の1つであるelixirで書かれたバックエンドです。これは並行スケーリングウェブソケットなどをすべて非常にうまく処理します。elixirで構築されたのは理にかなっています。私はファンボーイなので、これは私が投資した理由の一部です。
これはポストデータベースを取得し、その一部を異なるクライアント間で同期するためのサーバーサイドインフラストラクチャです。彼らはそれをホストするためのクラウド製品を持っています。そして最も重要な部分はPG、PG light、リアルタイムのリアクティブバインディングを持つ軽量なPostgressへの同期です。これは彼らがSQLite WASMプロジェクトから多くを学んで構築したライブラリです。
PGiteの目標は、Postgress クエリを実行し、Postgress データとパターンをブラウザで複製できるようにすることです。そのため、データベースのサブセクションを取得してブラウザに配置し、クエリを実行できます。そしてWomで動作します。つまり、WSMがサポートされているどこでも動作します。
これらすべてがElectricのTypeScriptクライアントと一緒になり、シェイプストリームを作成できます。彼らのシェイプの概念は、スキーマの中のテーブルのような何かに対する抽象化であり、テーブルとそこから取得できる部分の形状を持つプリミティブを提供します。
ここではこのURLからこれらのパラメータでシェイプを作成します。次に、ここから影響を受ける変更が発生したときに自動的に更新するか、そのサブスクライブ関数を呼び出すシェイプストリームからシェイプを作成できます。これを基に、バックエンドでElectricに接続し、クエリに関連する何かが変更されたときにそれを確認し、それが発生したときにこの更新を発火する強力な同期エンジンを構築できます。
しかし、このコードを見れば分かるように、アプリケーションにこれらすべての同期エンジンとレイヤーを自分で構築するのはあまり楽しくありません。では、どうすればよいでしょうか?ほとんどの人はReactライブラリを構築します。そのため、Electric SQL Reactがあります。これは、特定のURLとそれに渡すパラメータをバックエンドのElectric SQLとバインドするためのReactフックで、クライアント上でそのデータを取得し、変更があったときに自動更新するようにします。
このパッケージにはほとんどコードがないと想像しますが、より重要なのは、このパッケージはおそらくあまりメンテナンスされていないということです。なぜなら、それは彼らのビジネスの中核ではないからです。彼らが販売しているものではないという意味で。彼らははるかにインフラ側に焦点を当てています。
では、それをどう処理しますか?Tanstack queryの形状を複製しようとしている20行のコードのReactパッケージがあるとしましょう。インフラ側と、楽観的更新がエコシステムでうまく解決されていないような中核的な問題に焦点を当てたいとします。Tanstack queryにビルドすることはできません。なぜなら、あなたの製品が解決するように構築されている問題を解決しないからです。
Electric SQLは複製されたDBです。変更をローカルに書き込み、すぐに更新することができます。しかし、楽観的に物事を行うことができない場合、崩壊します。彼らはここにTanstack queryを使用した例を持っています。それらがすぐに超複雑になるのが分かります。なぜなら、クエリからのデータにそれを追加する代わりに、一時的なミューテーションデータを表示するためのuse mutation stateを含む、これらのクレイジーな追加フックをすべて書く必要があるからです。
今、彼らはレンダーでケースを持っています。クリアリング状態にない場合、クエリからのものを取得し、ミューテーションからのものを追加するアイテムのマップを作成します。そのため、この楽観的更新をトリガーするには、レンダーコードが得る必要があります。これは厳しいです。そして、それが彼らがReact Queryでの楽観的更新を修正するよう、TannerとTKを脅すために連絡を取ることを決めた時点です。
代わりに、彼らはローカルでの楽観的更新にこれほど注目することが大きなギャップであることを認識し、新しい解決策を提案しました。それがTanstack Optimisticでした。そして今、私たちは今日話すべきことに辿り着きました。Tanstack Optimisticは、Tanstack DBに名前が変更されました。
彼らがどのように説明しているか見てみましょう。まず第一に、Tanstack DBは同期上に超高速アプリを構築するための反応的なクライアントストアです。コレクション、ライブクエリ、楽観的ミューテーションを備えたTanackqueryを拡張し、UIをリアクティブ、一貫性があり、超高速に保ちます。Primeのリファレンス、これが存在する理由をうまく説明しているTwitterの投稿があります。
DBは、タナックエリーを拡張する反応的、正規化されたトランザクション状態エンジンです。ライブクエリを持つコレクション、差分データフローによるサブミリ秒の増分更新、最小限の再レンダリングのための細かい粒度の反応性、同期ライフサイクルサポートを持つ堅牢な楽観的トランザクション、デフォルトで正規化されたデータを提供します。
タナックエリーがクライアント側データベースに会うと考えてください。ローカルファースト、コラボレーティブ、またはオフラインフレンドリーなアプリに最適です。自分でこれを構築しようとした人として、これは非常に素晴らしく聞こえます。同期エンジンの構築は地獄でした。
これはバックエンドに依存せず、段階的に採用可能であり、REST、GraphQL、同期エンジン、ポーリングで動作します。このサークルでGraphQLがよく出てくるのは、Electricの共同創設者の一人がKyleで、Gatsbyの作成者でもあるからです。これがどこに行くかについての私の考えに何をもたらすかについては、これ以上コメントしません。
Tanstack queryとのシームレスなペアリングは、私が最も興奮している部分です。では、使用するとどのように見えるでしょうか?まず、データを実際に取得するクエリキークエリ関数、このtodosのコレクションから項目を一意に識別できるようにするためのID、そしてスキーマを持つクエリコレクションを作成する必要があります。標準的なスキーマは、zod validot arch typeなどのようなものを考えてください。
これで、tanstack/react-dbからライブクエリからデータを取得できます。これはtodoコレクションからのクエリで、スキーマで定義されたフィールドであるcompletedがfalseに等しいものです。これにより、完了していないすべてのtodosが取得できます。しかし、ここにはいくつかの落とし穴があります。私が見ているのは、これは何ですか?ここで構築されている新しいクエリ構文がまた1つあります。なぜ私がそれについて懐疑的なのかについては後で説明します。
今話したいのは楽観的な部分です。今、use optimistic mutation hookがあります。const collections, modified new to-do。このトランザクションがあります。トランザクションを取得しています。Mutations zeroトランザクションのリストから最初のものを取得していますが、存在しない可能性もあります。to-doの後に感嘆符がついています。すでにタイプセーフティのポイントを失っています。次にAPI.todos.create new to-doを待ち、collection.invalidateを待ちます。おや?これは先ほど行っていたことすべてではありません。
これはどこで操作しているものを取得するのでしょうか?あぁ、クリック時のadd to-do.mutateです。これには行内でトランザクションが渡されています。つまり、この楽観的ミューテーションが対応するミューテーションを書く必要はありません。これを汎用的に呼び出し、実際に行いたいミューテーションと操作したいコレクションを渡します。
実装の詳細がここで漏れているのはそのためです。これが私が混乱している理由です。一方では、collectionが何であるかが分かるのでより型安全です。しかし他方では、これはReact Queryの問題を10倍悪くしています。React Queryでは、特定のuse mutation呼び出しには、それにバインドされた1つのミューテーション関数があります。
特定のto-doミューテーションは、ミューテーションの動作と楽観的更新の動作を密接に結びつけます。なぜなら、それらは同じミューテーションであり、そのミューテーションは1つのことができるからです。もし違うトランザクションをadd to-doに渡したらどうなるでしょうか?すべてが非常に急速に崩壊します。
もし違うものを渡していたら、例えばupdate to-doを渡して誤ってこれを呼び出した場合、あるいはadd to mutate to-doではなく別の名前にしていた場合、この中でto-doを作成していますが、誤って別のことをした場合、非常に悪い状況になります。これらの実装の詳細がここからここへ漏れていることは、1つの場所で定義して他の場所で呼び出すのではなく、恐ろしいことです。
更新とロジックが近いほど、生活は楽になります。そして再びConverを推薦しているようで申し訳ないのですが、Convexがうまくやっていることの1つは、このクライアント側のフロントエンドコードがあり、既存のスレッドがここで発射したクエリから取得されていることです。その型定義はここにあります。
さらに重要なのは、コマンドクリックするとこれを実際に行うバックエンド関数に移動することです。はい、彼らもある程度奇妙な構文を持っています。奇妙な構文はインデックスでヒットする方法です。面白いのは、ここでのように親をスレッドにリンクするようなデータをリンクしたい場合です。
SQLのようにクエリのセマンティクスにそれを含めると思うかもしれませんが、convexの仕組みのため、私は文字通りそれらをマップしているか、親を取得するためのフェッチを待ちます。はい、本当です。データをただマップするか、各行のクエリをfor loopで行います。これは狂っているように見えますが、convexの仕組みはこれらすべてを単一のトランザクションにフラット化します。
そのため、それでも非常に高速になります。通常より入れ子が多いです。チャットでその指摘をしている人がいます。その通りです。しかし、完全にタイプセーフであり、ただのJavaScriptです。ここに書いたものはすべて普通のJSやTSで、特別なものは何もありません。
クールなことの1つは、IDに使用する文字列がブランド化されていることです。そのため、thread.branch parent thread IDがスレッドIDであることを知っています。そのため、branch parentがスレッドであることを知っています。代わりにメッセージIDを渡した場合、これはメッセージだと分かります。そのため、各ステップで完全なタイプ安全性が得られます。
そして私がここでクライアントにいるとき、update thread mutationには呼び出せる1つの関数があります。そして楽観的更新は呼び出せる1つの関数に直接結びついています。これにより、ここで問題となる可能性があるドリフトの多くが防止されます。そして私が見たときに心配なのは、クライアント上での更新ロジックと、その更新を行うものに渡す物事の形状との間にドリフトが発生する可能性があることです。
さらに読み進めましょう。コレクションはinsert、update、deleteの操作をサポートしています。これらの操作はトランザクショナルミューテーターのコンテキスト内で行う必要があります。そのため、mutatorであるoptimistic mutationを作成し、mutatorにはトランザクションを渡す必要があります。
楽観的状態を即座に適用するmutate関数をトリガーします。to-do collection.update to-do, draft draft.completed equals trueのようにします。コレクションデータを直接変更するのではなく、コレクションは内部で同期およびロードされたデータを不変として扱い、別のローカルミューテーションのセットを楽観的状態として維持します。
ライブクエリがコレクションから読み取るとき、不変の同期データの上にローカルの楽観的ミューテーションを重ねたローカルビューを参照します。つまり、実際にはサーバーからのデータを更新していません。そのコピーを作成して変更し、サーバーから実際のデータが返ってくるのを待つ間、一時的にその上に表示しています。
面白いことに、私も実際にこれを行っています。use thread dataフックは、サイドバーにリストされているスレッドを取得するためにsession queryを呼び出すフックです。それらをConvexから取得します。そのためthreads from Convexがあります。
Convexがフックで好きでないことの1つは、loading data error statesを返さないことです。彼らは単にfallbackとしてundefinedを使用して物事だけを返します。そのため、それがロード中かどうかを知る唯一の方法は、それがundefinedかどうかです。そして実際にundefinedを返せる場合、幸運を祈ります。
私はConvexが大好きです。これは最悪です。彼らはこれが最悪であることを知っています。修正するのは楽しみです。そのため、ベータ版をロードするときに見られるように、ページをロードすると、サイドバーがすぐにそこにあるようにするために行っていることは、結果をローカルストレージにバックアップし、変更があるたびにローカルストレージの結果を更新することです。
そのため、最初のペイントでは、まだConvexからスレッドを取得していない場合、ローカルストレージの値を返すことができます。これは、サーバーがデータで応答するのを待つ全体をスキップして、ユーザーに何かをより速く表示するための試みです。
これは楽観的ミューテーションと同じではありませんが、非常に似ています。そして伝えたいポイントは、threads from local storageというこのカスタムデータのことです。これはセッションクエリからバックエンドから期待されるものと同じ形状です。ここではConvexからスレッドを最初に返し、それがない場合はローカルストアにフォールバックします。
これを反転させたと想像してください。threads from local storageが最初に返され、threads from convexがフォールバックです。ここで変更を加えると、これが最初に表示されます。そしてこれが完了したとき、それを破棄します。threads from convexの変更があると、ローカルストアを消去します。これがこの仕組みです。
ここでの魔法は、サーバーからの状態が純粋なままであることです。それは常にサーバーが返す正確なものです。一時的にその上に鏡があるだけです。そして新しいデータがあるため、それをもうユーザーに返したくないと決めた瞬間、単にそれを破棄します。これは非常に理にかなったパターンであり、彼らがそれを採用するのを見て興奮しています。
しかし、実装の詳細がどれだけ漏れているか興味があります。これは最悪です。上部で定義したフックとは別のアプリケーションロジックのどこかでこれを渡さなければならないという考えは好きではありません。本当に好きではありません。おっ、Convexがここにいますね。会えて嬉しいです。
ずっとただConverをほめるだけにならないように努力してきました。はい、以前にもConverが楽観的更新をどのように行うかを褒めました。これまでに扱った中で最も痛みが少ないものです。そしてこれがその道を行くことを期待していたにもかかわらず、私は懐疑的です。
ドキュメントに戻りましょう。ローカルミューテーションはミューテーターを作成するときに渡される非同期ミューテーション関数に渡されます。このミューテーション関数は書き込みを処理する責任があり、通常はサーバーまたはデータベースに送信することで行います。
Tanstack DBはローカル書き込みが行われたときに適用された楽観的状態を削除する前に、関数が解決するのを待ちます。ここのコードを見て考えるかもしれないもう一つのことは、私もConverでこれを持っていることです。更新ロジックは異なるファイルにあります。
editable thread titleがここにあるなら、スレッドタイトルを実際に更新するコードはコマンドクリックして取得する必要があります。そしてこれは全く異なる関数です。全く異なるものがたくさんあります。それは同じことではないのでしょうか?これはここから漏れている実装の詳細です。
いいえ、それはバックエンドロジックだからです。そしてこれも同じ問題があります。これらのすべてのコードを見て、これらすべてのことを行うために、ミューテーションのためにこれらすべてを書いたにもかかわらず、バックエンドロジックもまだ書かれる必要があります。
コメント