データ取得ライブラリ (react-query、swr、rtk-query など) の自動正規化とデータ更新
導入
モチベーション
インストール
必要な条件
配列の正規化
デバッグ
パフォーマンス
統合
例
normy
、アプリケーション データを自動的に正規化できるようにするライブラリです。データが正規化されると、多くの場合、データは自動的に更新されます。
normy
のコア、つまり@normy/core
ライブラリは、アプリケーションで直接使用することを意図したものではなく、お気に入りのデータ取得ライブラリと簡単に統合できるロジックを内部に持っています。すでに、 react-query
、 swr
、 RTK Query
との正式な統合が行われています。別の取得ライブラリを使用すると、Github の問題が発生する可能性があるため、同様に追加される可能性があります。
normy
実際に何を行うかを理解するには、例を見るのが最善です。 react-query
使用すると仮定しましょう。次に、次の方法でコードをリファクタリングできます。
「react」から React をインポートします。 輸入 { クエリクライアントプロバイダー、 クエリクライアント、 useQueryClient、 } from '@tanstack/react-query';+ import { QueryNormalizerProvider } from '@normy/react-query'; const queryClient = 新しいQueryClient(); const 書籍 = () => { const queryClient = useQueryClient(); const { データ:booksData = [] } = useQuery(['books'], () => Promise.resolve([ { id: '1'、名前: '名前 1'、作成者: { id: '1001'、名前: 'User1' } }、 { id: '2'、名前: '名前 2'、作成者: { id: '1002'、名前: 'User2' } }、 ])、 ); const { データ: bookData } = useQuery(['book'], () => Promise.resolve({ ID: '1'、 名前: '名前 1'、 作成者: { ID: '1001'、名前: 'User1' }、 })、 ); const updateBookNameMutation = useMutation({ 突然変異Fn: () => ({ ID: '1'、 名前: '名前 1 が更新されました', }),- onSuccess: mutationData => {- queryClient.setQueryData(['books'], data =>- data.map(book =>- book.id === mutationData.id ? { ...book, . ..mutationData } : book,- ),- );- queryClient.setQueryData(['book'], data =>- data.id === mutationData.id ? { ...data, ...mutationData } : データ、- );- }、}); const updateBookAuthorMutation = useMutation({ 突然変異Fn: () => ({ ID: '1'、 作成者: { ID: '1004'、名前: 'User4' }、 }),- onSuccess: mutationData => {- queryClient.setQueryData(['books'], data =>- data.map(book =>- book.id === mutationData.id ? { ...book, . ..mutationData } : book,- ),- );- queryClient.setQueryData(['book'], data =>- data.id === mutationData.id ? { ...data, ...mutationData } : データ、- );- }、}); const addBookMutation = useMutation({ 突然変異Fn: () => ({ ID: '3'、 名前: '名前 3'、 作成者: { ID: '1003'、名前: 'User3' }、 })、 // 最上位の配列を含むデータの場合は、データを手動で更新する必要があります onSuccess: mutationData => { queryClient.setQueryData(['books'], data => data.concat(mutationData)); }、 }); // JSX を返します }; const App = () => (+ <QueryNormalizerProvider queryClient={queryClient}> <QueryClientProvider client={queryClient}> <書籍> </QueryClientProvider>+ </QueryNormalizerProvider> );
したがって、ご覧のとおり、最上位の配列を除けば、手動でデータを更新する必要はもうありません。これは、特定のミューテーションによって複数のクエリのデータを更新する必要がある場合に特に便利です。更新を手動で行うのは冗長であるだけでなく、どのクエリを更新するかを正確に把握する必要があります。クエリが多ければ多いほど、 normy
もたらすメリットは大きくなります。
どのように機能するのでしょうか?デフォルトでは、 id
キーを持つすべてのオブジェクトは ID ごとに編成されます。これで、キーid
持つすべてのオブジェクトが正規化されます。これは、単に ID によって保存されることを意味します。同じ ID を持つ一致するオブジェクトがすでに存在する場合、新しいオブジェクトは、すでにその状態にあるオブジェクトと深くマージされます。したがって、突然変異からのサーバー応答データが{ id: '1', title: 'new title' }
の場合、このライブラリは自動的にそれを判断して、すべての依存クエリに対してid: '1'
を持つオブジェクトのtitle
を更新します。
また、深さに関係なく、ID を持つネストされたオブジェクトでも機能します。 ID を持つオブジェクトに ID を持つ他のオブジェクトがある場合、それらは個別に正規化され、親オブジェクトはそれらのネストされたオブジェクトへの参照のみを持ちます。
パッケージをインストールするには、次を実行するだけです。
$ npm install @normy/react-query
または、CDN: https://unpkg.com/@normy/react-query
を使用することもできます。
パッケージをインストールするには、次を実行するだけです。
$ npm install @normy/swr
または、CDN: https://unpkg.com/@normy/swr
を使用することもできます。
パッケージをインストールするには、次を実行するだけです。
$ npm install @normy/rtk-query
または、CDN: https://unpkg.com/@normy/rtk-query
を使用することもできます。
react-query
、 swr
、 rtk-query
以外のライブラリにプラグインを書きたい場合:
$ npm install @normy/core
または、CDN: https://unpkg.com/@normy/core
を使用することもできます。
プラグインの作成方法を確認するには、今のところ@normy/react-query
のソース コードを確認するだけです。非常に簡単に実行できます。将来的にはガイドが作成される予定です。
自動正規化を機能させるには、次の条件を満たす必要があります。
オブジェクトを識別する標準化された方法が必要です。通常、これはキーid
によって行われます。
ID はオブジェクト タイプ間だけでなく、アプリ全体で一意である必要があります。そうでない場合は、ID に何かを追加する必要があります。GraphQL の世界でも同じことを行う必要があり、通常は_typename
を追加します。
同じ ID を持つオブジェクトは一貫した構造を持つ必要があります。あるクエリ内の書籍などのオブジェクトにtitle
キーがある場合、他のクエリではそれがtitle
である必要があり、突然name
付けられることはありません。
これらの要件を満たすためにcreateQueryNormalizer
に渡すことができる関数、つまりgetNormalizationObjectKey
があります。
getNormalizationObjectKey
最初の点で役立ちます。たとえば、 _id
キーなど、オブジェクトを別の方法で識別する場合は、 getNormalizationObjectKey: obj => obj._id
を渡すことができます。
getNormalizationObjectKey
と、2 番目の要件を渡すこともできます。たとえば、ID が一意であるものの、アプリ全体ではなくオブジェクト タイプ内で一意である場合は、 getNormalizationObjectKey: obj => obj.id && obj.type ? obj.id + obj.type : undefined
を使用できます。 getNormalizationObjectKey: obj => obj.id && obj.type ? obj.id + obj.type : undefined
またはそれに類似したもの。それが不可能な場合は、次のように自分でサフィックスを計算することもできます。
const getType = obj => { if (obj.bookTitle) {return 'book'; } if (obj.surname) {return 'user'; } 未定義を返します;};createQueryNormalizer(queryClient, { getNormalizationObjectKey: obj =>obj.id && getType(obj) && obj.id + getType(obj),});
ポイント 3 は常に満たされている必要がありますが、満たされていない場合は、バックエンド開発者に物事を標準化して一貫性を保つように依頼する必要があります。最後の手段として、あなた側で回答を修正することができます。
残念ながら、データを手動で更新する必要がなくなるという意味ではありません。一部の更新は、通常と同様に手動で行う必要があります。つまり、配列への項目の追加と削除です。なぜ? REMOVE_BOOK
突然変異を想像してください。この本は多くのクエリに存在する可能性がありますが、ライブラリはどのクエリから削除したいのかを知ることができません。同じことがADD_BOOK
にも当てはまります。ライブラリは、ブックをどのクエリに追加する必要があるのか、さらにはどの配列インデックスとして追加する必要があるのかを知ることができません。 SORT_BOOKS
のようなアクションについても同じです。ただし、この問題は最上位の配列にのみ影響します。たとえば、何らかの ID と、 likedByUsers
のような別のキーを持つ書籍がある場合、 likedByUsers
の更新されたリストを含む新しい書籍を返すと、これは再び自動的に機能します。
ただし、ライブラリの将来のバージョンでは、いくつかの追加ポインタを使用して、上記の更新も行うことができるようになります。
normy
データ操作が実際にどのようなことを行うかに興味がある場合は、 devLogging
オプションを使用できます。
<QueryNormalizerProvider queryClient={クエリクライアント} NormalizerConfig={{ devLogging: true }}> {children}</QueryNormalizerProvider>
デフォルトではfalse
ですが、 true
に設定すると、クエリが設定または削除されたときにコンソール情報が表示されます。
これは開発環境でのみ機能することに注意してください。 true
を渡しても、本番環境ではログは記録されません (正確にprocess.env.NODE_ENV === 'production'
の場合)。 NODE_ENV
は通常、 webpack
などのモジュール バンドラーによって設定されるため、おそらくNODE_ENV
自分で設定することを心配する必要はありません。
いつものことですが、自動化にはコストがかかります。将来的にはいくつかのベンチマークが追加される可能性がありますが、現時点では手動テストにより、データ内に数万の正規化されたオブジェクトがない限り、オーバーヘッドは目立たないことが示されています。ただし、パフォーマンスを向上させる柔軟な方法がいくつかあります。
正規化できるのは、データ更新を伴うクエリと、データを更新する必要がある変更のみです。つまり、データの一部のみを正規化できます。その方法については、統合ドキュメントを確認してください。
1.
と似ていますが、非常に大きなデータを含むクエリとミューテーション用です。
組み込みの最適化機能があり、突然変異応答からのデータが正規化されたストア内のデータと実際に異なるかどうかをチェックします。同じである場合、依存クエリは更新されません。したがって、ミューテーション データには、実際に異なる可能性のあるもののみを含めることが適切です。これにより、不必要な正規化やクエリの更新を防ぐことができます。
サポートするライブラリでは、 structuralSharing
オプションを無効にしないでください。更新後のクエリ データが更新前と参照的に同じである場合、このクエリは正規化されません。これは、特にリフォーカス時の再フェッチ後、複数のクエリを同時に (通常はまったく同じデータに) 更新する可能性があるため、パフォーマンスが大幅に最適化されます。
getNormalizationObjectKey
関数を使用して、どのオブジェクトを実際に正規化する必要があるかをグローバルに設定できます。例えば:
<QueryNormalizerProvider queryClient={クエリクライアント} NormalizerConfig={{getNormalizationObjectKey: obj => (obj.normalizable ? obj.id : 未定義), }}> {children}</QueryNormalizerProvider>
さらに、将来的には、パフォーマンス固有のオプションがいくつか追加される予定です。
現在、データ取得ライブラリとの正式な統合は、 react-query
、 swr
、 rtk-query
の 3 つです。特定の統合については、専用のドキュメントを参照してください。
反応クエリ
swr
rtkクエリ
このパッケージを実際のアプリケーションでどのように使用できるかをサンプルで試してみることを強くお勧めします。
現在、次のような例があります。
反応クエリ
トロピカル
swr
rtkクエリ
マサチューセッツ工科大学