プリフェッチ
プリフェッチの目的は、ユーザーがページに移動したり、既知のコンテンツを読み込もうとする*前に*データを取得することです。
これを実行したい状況はいくつかありますが、非常に一般的なユースケースは次のとおりです。
- ユーザーがナビゲーション要素にカーソルを合わせる
- ユーザーがリンクであるリスト要素にカーソルを合わせる
- ユーザーが次のページネーションボタンにカーソルを合わせる
- ユーザーがページに移動し、ツリーの下にある一部のコンポーネントが上記のデータ yêu cầuすることを知っている場合。 これにより、フェッチのウォーターフォールを防ぐことができます。
React Hooks を使用したプリフェッチ
useMutation
フックと同様に、usePrefetch
フックは自動的には実行されません。動作を開始するために使用できる「トリガー関数」を返します。
2 つの引数を受け取ります。1 つ目は、API サービスで定義したクエリアクションのキーで、2 つ目は 2 つのオプションパラメータのオブジェクトです。
export type PrefetchOptions =
| { force?: boolean }
| {
ifOlderThan?: false | number;
};
usePrefetch<EndpointName extends QueryKeys<Definitions>>(
endpointName: EndpointName,
options?: PrefetchOptions
): (arg: QueryArgFrom<Definitions[EndpointName]>, options?: PrefetchOptions) => void;
フックの動作のカスタマイズ
フックを宣言するとき、または呼び出しサイトで、これらのプリフェッチオプションを指定できます。 呼び出しサイトはデフォルトよりも優先されます。
ifOlderThan
- (デフォルト:false
|number
) - *number は秒単位の値*- 指定されている場合、
new Date()
と最後のfulfilledTimeStamp
の差が指定された値よりも大きい場合にのみクエリを実行します。
- 指定されている場合、
force
force: true
の場合、ifOlderThan
値が設定されていても無視され、キャッシュに存在する場合でもクエリが実行されます。
トリガー関数の動作
- トリガー関数は*常に*
void
を返します。 - 宣言時または呼び出しサイトで
force: true
が設定されている場合、クエリは何があっても実行されます。 その例外は、同じクエリがすでに実行中の場合です。 - オプションが指定されておらず、クエリがキャッシュに存在する場合、クエリは実行されません。
- オプションが指定されておらず、クエリがキャッシュに*存在しない*場合、クエリは実行されます。
- プリフェッチしているのと同じクエリにサブスクライブされている
useQuery
フックがツリーに*あると仮定*するとuseQuery
は{isLoading: true, isFetching: true, ...rest
} を返します
- プリフェッチしているのと同じクエリにサブスクライブされている
ifOlderThan
が指定されているが false と評価され、クエリがキャッシュにある場合、クエリは実行されません。ifOlderThan
が指定されていて true と評価される場合、既存のキャッシュエントリがあってもクエリが実行されます。- プリフェッチしているのと同じクエリにサブスクライブされている
useQuery
フックがツリーに*あると仮定*するとuseQuery
は{isLoading: false, isFetching: true, ...rest
} を返します
- プリフェッチしているのと同じクエリにサブスクライブされている
function User() {
const prefetchUser = usePrefetch('getUser')
// Low priority hover will not fire unless the last request happened more than 35s ago
// High priority hover will _always_ fire
return (
<div>
<button onMouseEnter={() => prefetchUser(4, { ifOlderThan: 35 })}>
Low priority
</button>
<button onMouseEnter={() => prefetchUser(4, { force: true })}>
High priority
</button>
</div>
)
}
レシピ: すぐにプリフェッチする
場合によっては、リソースをすぐにプリフェッチしたい場合があります。 これは数行のコードで実装できます。
type EndpointNames = keyof typeof api.endpoints
export function usePrefetchImmediately<T extends EndpointNames>(
endpoint: T,
arg: Parameters<(typeof api.endpoints)[T]['initiate']>[0],
options: PrefetchOptions = {},
) {
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(api.util.prefetch(endpoint, arg as any, options))
}, [])
}
// In a component
usePrefetchImmediately('getUser', 5)
Hooks を使用しないプリフェッチ
usePrefetch
フックを使用していない場合は、任意のフレームワークで同じ動作を独自に再現できます。
以下に示すように prefetch
サンクをディスパッチすると、ここで説明したのとまったく同じ動作が見られます。
store.dispatch(
api.util.prefetch(endpointName, arg, { force: false, ifOlderThan: 10 }),
)
クエリアクションをディスパッチすることもできますが、追加のロジックを実装する必要があります。
dispatch(api.endpoints[endpointName].initiate(arg, { forceRefetch: true }))
プリフェッチの例
基本的なプリフェッチ
これは、ユーザーが次の矢印にカーソルを合わせたときにプリフェッチする方法を示す非常に基本的な例です。 これはおそらく最適な解決策ではありません。なぜなら、ユーザーがカーソルを合わせてクリックし、マウスを動かさずにページを変更した場合、次の onMouseEnter
イベントが表示されないため、次のページをプリフェッチする必要があることがわからないからです。 この場合、これを自分で処理する必要があります。 次のページを自動的にプリフェッチすることも検討できます...
自動プリフェッチ
最後の例に続いて、次のページを自動的に `prefetch` し、ネットワーク遅延がないように見せます。
すべての既知のページのプリフェッチ
useQuery
によって初期化された最初のクエリが実行された後、残りのすべてのページを自動的にフェッチします。