about summary refs log tree commit diff
path: root/src/lib/react-query.tsx
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-04-03 23:53:44 +0100
committerGitHub <noreply@github.com>2024-04-03 23:53:44 +0100
commit295a0949f44ef332c7b33e3cd03d0c2ea89047cf (patch)
tree96b6cc9ed615c9c7df6ba50628b92e8266fe489a /src/lib/react-query.tsx
parent6c728f79dea65d9deeb57a430e6c9fad39940316 (diff)
downloadvoidsky-295a0949f44ef332c7b33e3cd03d0c2ea89047cf.tar.zst
Factor out QueryProvider (#3331)
Diffstat (limited to 'src/lib/react-query.tsx')
-rw-r--r--src/lib/react-query.tsx89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/lib/react-query.tsx b/src/lib/react-query.tsx
new file mode 100644
index 000000000..08b61ee20
--- /dev/null
+++ b/src/lib/react-query.tsx
@@ -0,0 +1,89 @@
+import React from 'react'
+import {AppState, AppStateStatus} from 'react-native'
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {createAsyncStoragePersister} from '@tanstack/query-async-storage-persister'
+import {focusManager, QueryClient} from '@tanstack/react-query'
+import {
+  PersistQueryClientProvider,
+  PersistQueryClientProviderProps,
+} from '@tanstack/react-query-persist-client'
+
+import {isNative} from '#/platform/detection'
+
+// any query keys in this array will be persisted to AsyncStorage
+const STORED_CACHE_QUERY_KEYS = ['labelers-detailed-info']
+
+focusManager.setEventListener(onFocus => {
+  if (isNative) {
+    const subscription = AppState.addEventListener(
+      'change',
+      (status: AppStateStatus) => {
+        focusManager.setFocused(status === 'active')
+      },
+    )
+
+    return () => subscription.remove()
+  } else if (typeof window !== 'undefined' && window.addEventListener) {
+    // these handlers are a bit redundant but focus catches when the browser window
+    // is blurred/focused while visibilitychange seems to only handle when the
+    // window minimizes (both of them catch tab changes)
+    // there's no harm to redundant fires because refetchOnWindowFocus is only
+    // used with queries that employ stale data times
+    const handler = () => onFocus()
+    window.addEventListener('focus', handler, false)
+    window.addEventListener('visibilitychange', handler, false)
+    return () => {
+      window.removeEventListener('visibilitychange', handler)
+      window.removeEventListener('focus', handler)
+    }
+  }
+})
+
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      // NOTE
+      // refetchOnWindowFocus breaks some UIs (like feeds)
+      // so we only selectively want to enable this
+      // -prf
+      refetchOnWindowFocus: false,
+      // Structural sharing between responses makes it impossible to rely on
+      // "first seen" timestamps on objects to determine if they're fresh.
+      // Disable this optimization so that we can rely on "first seen" timestamps.
+      structuralSharing: false,
+      // We don't want to retry queries by default, because in most cases we
+      // want to fail early and show a response to the user. There are
+      // exceptions, and those can be made on a per-query basis. For others, we
+      // should give users controls to retry.
+      retry: false,
+    },
+  },
+})
+
+const asyncStoragePersister = createAsyncStoragePersister({
+  storage: AsyncStorage,
+  key: 'queryCache',
+})
+
+const dehydrateOptions: PersistQueryClientProviderProps['persistOptions']['dehydrateOptions'] =
+  {
+    shouldDehydrateMutation: (_: any) => false,
+    shouldDehydrateQuery: query => {
+      return STORED_CACHE_QUERY_KEYS.includes(String(query.queryKey[0]))
+    },
+  }
+
+const persistOptions = {
+  persister: asyncStoragePersister,
+  dehydrateOptions,
+}
+
+export function QueryProvider({children}: {children: React.ReactNode}) {
+  return (
+    <PersistQueryClientProvider
+      client={queryClient}
+      persistOptions={persistOptions}>
+      {children}
+    </PersistQueryClientProvider>
+  )
+}