Add some more clarity to the RQ docs (#10120)

This commit is contained in:
Eric Bailey
2026-04-06 18:45:40 -05:00
committed by GitHub
parent 011d8d2f7c
commit 99257f2816
7 changed files with 152 additions and 61 deletions
+18 -11
View File
@@ -22,13 +22,10 @@ import {
import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {
PERSISTED_QUERY_GCTIME,
PERSISTED_QUERY_ROOT,
STALE,
} from '#/state/queries'
import {GCTIME, STALE} from '#/state/queries'
import {RQKEY as listQueryKey} from '#/state/queries/list'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {createQueryKey} from '#/state/queries/util'
import {useAgent, useSession} from '#/state/session'
import {router} from '#/routes'
import {useModerationOpts} from '../preferences/moderation-opts'
@@ -416,10 +413,20 @@ const PWI_DISCOVER_FEED_STUB: SavedFeedSourceInfo = {
contentMode: undefined,
}
const createPinnedFeedInfosQueryKeyRoot = (
const createPinnedFeedInfosQueryKey = (
kind: 'pinned' | 'saved',
feedUris: string[],
) => [PERSISTED_QUERY_ROOT, 'feed-info', kind, feedUris]
) =>
createQueryKey(
'feed-info',
{
kind,
feedUris,
},
{
persistedVersion: 1,
},
)
export function usePinnedFeedsInfos() {
const {hasSession} = useSession()
@@ -428,11 +435,11 @@ export function usePinnedFeedsInfos() {
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
return useQuery({
queryKey: createPinnedFeedInfosQueryKeyRoot(
queryKey: createPinnedFeedInfosQueryKey(
'pinned',
pinnedItems.map(f => f.value),
),
gcTime: PERSISTED_QUERY_GCTIME,
gcTime: GCTIME.INFINITY,
staleTime: STALE.INFINITY,
enabled: !isLoadingPrefs,
queryFn: async () => {
@@ -536,11 +543,11 @@ export function useSavedFeeds() {
const queryClient = useQueryClient()
return useQuery({
queryKey: createPinnedFeedInfosQueryKeyRoot(
queryKey: createPinnedFeedInfosQueryKey(
'saved',
savedItems.map(f => f.value),
),
gcTime: PERSISTED_QUERY_GCTIME,
gcTime: GCTIME.INFINITY,
staleTime: STALE.INFINITY,
enabled: !isLoadingPrefs,
placeholderData: previousData => {
+3 -19
View File
@@ -19,22 +19,6 @@ export const STALE = {
INFINITY: Infinity,
}
/**
* Root key for persisted queries.
*
* If the `querykey` of your query uses this at index 0, it will be
* persisted automatically by the `PersistQueryClientProvider` in
* `#/lib/react-query.tsx`.
*
* Be careful when using this, since it will change the query key and may
* break any cases where we call `invalidateQueries` or `refetchQueries`
* with the old key.
*
* Also, only use this for queries that are safe to persist between
* app launches (like user preferences).
*
* Note that for queries that are persisted, it is recommended to extend
* the `gcTime` to a longer duration, otherwise it'll get busted
*/
export const PERSISTED_QUERY_ROOT = 'PERSISTED'
export const PERSISTED_QUERY_GCTIME = Infinity
export const GCTIME = {
INFINITY: Infinity,
}
+6 -12
View File
@@ -3,15 +3,12 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {z} from 'zod'
import {MAX_LABELERS} from '#/lib/constants'
import {
PERSISTED_QUERY_GCTIME,
PERSISTED_QUERY_ROOT,
STALE,
} from '#/state/queries'
import {GCTIME, STALE} from '#/state/queries'
import {
preferencesQueryKey,
usePreferencesQuery,
} from '#/state/queries/preferences'
import {createQueryKey} from '#/state/queries/util'
import {useAgent} from '#/state/session'
const labelerInfoQueryKeyRoot = 'labeler-info'
@@ -26,11 +23,8 @@ export const labelersInfoQueryKey = (dids: string[]) => [
dids.slice().sort(),
]
const persistedLabelersDetailedInfoQueryKey = (dids: string[]) => [
PERSISTED_QUERY_ROOT,
'labelers-detailed-info',
dids,
]
const createLabelersDetailedInfoQueryKey = (dids: string[]) =>
createQueryKey('labelers-detailed-info', {dids}, {persistedVersion: 1})
export function useLabelerInfoQuery({
did,
@@ -69,8 +63,8 @@ export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) {
const agent = useAgent()
return useQuery({
enabled: !!dids.length,
queryKey: persistedLabelersDetailedInfoQueryKey(dids),
gcTime: PERSISTED_QUERY_GCTIME,
queryKey: createLabelersDetailedInfoQueryKey(dids),
gcTime: GCTIME.INFINITY,
staleTime: STALE.MINUTES.ONE,
queryFn: async () => {
const res = await agent.app.bsky.labeler.getServices({
+8 -7
View File
@@ -9,11 +9,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {PROD_DEFAULT_FEED} from '#/lib/constants'
import {replaceEqualDeep} from '#/lib/functions'
import {getAge} from '#/lib/strings/time'
import {
PERSISTED_QUERY_GCTIME,
PERSISTED_QUERY_ROOT,
STALE,
} from '#/state/queries'
import {GCTIME, STALE} from '#/state/queries'
import {
DEFAULT_HOME_FEED_PREFS,
DEFAULT_LOGGED_OUT_PREFERENCES,
@@ -23,6 +19,7 @@ import {
type ThreadViewPreferences,
type UsePreferencesQueryResponse,
} from '#/state/queries/preferences/types'
import {createQueryKey} from '#/state/queries/util'
import {useAgent} from '#/state/session'
import {saveLabelers} from '#/state/session/agent-config'
import {useAgeAssurance} from '#/ageAssurance'
@@ -33,7 +30,11 @@ export * from '#/state/queries/preferences/const'
export * from '#/state/queries/preferences/moderation'
export * from '#/state/queries/preferences/types'
export const preferencesQueryKey = [PERSISTED_QUERY_ROOT, 'getPreferences']
export const preferencesQueryKey = createQueryKey(
'getPreferences',
{},
{persistedVersion: 1},
)
export function usePreferencesQuery() {
const agent = useAgent()
@@ -44,7 +45,7 @@ export function usePreferencesQuery() {
structuralSharing: replaceEqualDeep,
refetchOnWindowFocus: true,
queryKey: preferencesQueryKey,
gcTime: PERSISTED_QUERY_GCTIME,
gcTime: GCTIME.INFINITY,
queryFn: async () => {
if (!agent.did) {
return DEFAULT_LOGGED_OUT_PREFERENCES
+55
View File
@@ -14,6 +14,61 @@ import {
import * as bsky from '#/types/bsky'
export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [
string,
T,
{
persistedVersion?: number
},
]
/**
* Helper method to ensure consistent query keys and key ordering
*/
export function createQueryKey<T extends Record<string, unknown>>(
/**
* The query key root. All queries must have a root.
*/
root: string,
/**
* Any arguments the query depends on, and if changed, should result in the query being refetched.
*/
args: T,
options: {
/**
* If provided, this indicates that the query is persisted and the version
* of the persisted query format.
*
* This is used to ensure that when we make breaking changes to the
* persisted query format, we can increment the version and avoid trying to
* read old persisted queries with the new format.
*
* If you're persisting your queries, you probably want to set `gcTime:
* GCTIME.INFINITY` for this query, otherwise it'll get busted immediately
* after being persisted.
*/
persistedVersion?: number
} = {},
): StructuredQueryKey<T> {
return [root, args, options] as const
}
export function isQueryPersisted(
queryKey: QueryKey,
): queryKey is StructuredQueryKey<Record<string, unknown>> {
return (
Array.isArray(queryKey) &&
queryKey.length === 3 &&
typeof queryKey[0] === 'string' &&
typeof queryKey[1] === 'object' &&
queryKey[1] !== null &&
typeof queryKey[2] === 'object' &&
queryKey[2] !== null &&
'persistedVersion' in queryKey[2] &&
typeof queryKey[2].persistedVersion === 'number'
)
}
export async function truncateAndInvalidate<T = any>(
queryClient: QueryClient,
queryKey: QueryKey,