Radzion
2 min readJan 4, 2023

👋 Watch on YouTube

We can immediately load app state by saving the react-query cache to local storage. Let's try it out!

react-query has a utility for persisting the state of your queryClient and its caches for later use.

We can import everything from react-query. For persist to work properly, we need to pass QueryClient a cache time value to override the default during hydration. It should be at least 24 hours. Let’s set it to 5 days. To exclude some queries from being persistent, we can pass a filter function to the shouldDehydrateQuery param. To make react-query work, we'll pass the clietn to the QueryClientProvider.

import { paddleQueryKey } from "membership/paddle/hooks/usePaddleSdk"
import { QueryClient, QueryKey } from "react-query"
import { createWebStoragePersistor } from "react-query/createWebStoragePersistor-experimental"
import { persistQueryClient } from "react-query/persistQueryClient-experimental"
import { MS_IN_DAY } from "utils/time"

const cacheTime = MS_IN_DAY * 5

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
cacheTime,
},
},
})

const localStoragePersistor = createWebStoragePersistor({
storage: window.localStorage,
})

const doNotPersistQueries: QueryKey[] = [paddleQueryKey]

persistQueryClient({
queryClient,
persistor: localStoragePersistor,
maxAge: cacheTime,
hydrateOptions: {},
dehydrateOptions: {
shouldDehydrateQuery: ({ queryKey }) => {
return !doNotPersistQueries.includes(queryKey)
},
},
})

I load everything the user needs with one query — userStateQuery. It's a productivity app, and there is not that much data coming from the back-end. I use graphQL query string as a key for react-query to force cache invalidation on change. The query is enabled only for a logged-in user. The query function makes a post request to the GraphQL API.

const userStateQuery = `
query userState($input: UserStateInput!) {
userState(input: $input) {
...
}
}
`

const remoteStateQueryKey = userStateQuery

interface Props {
children: ReactNode
}

export const RemoteStateProvider = ({ children }: Props) => {
const isLoggedIn = useIsUserLoggedIn()

const queryClient = useQueryClient()

const dispatch = useDispatch()

const { data = null } = useQuery(
remoteStateQueryKey,
async () => {
const remoteState: RemoteStateView = await postToMainApi({
query: userStateQuery,
variables: {
input: {
timeZone: offsetedUtils.getOffset(),
},
},
})

return remoteState
},
{
keepPreviousData: true,
refetchOnMount: false,
refetchOnReconnect: false,
staleTime: Infinity,
enabled: isLoggedIn,
}
)

const updateState = useCallback(
(pieceOfState: Partial<RemoteStateView>) => {
queryClient.setQueryData<RemoteStateView>(
remoteStateQueryKey,
(state) => ({
...((state || {}) as RemoteStateView),
...pieceOfState,
})
)
},
[queryClient]
)

return (
<RemoteStateContext.Provider value={{ state: data, updateState }}>
{children}
</RemoteStateContext.Provider>
)
}

When the user signs out from the app, I clear all the cache.

Now, when we reload the app, there’s no waiting, everything loads immediately!

Radzion
Radzion

Written by Radzion

Crafting increaser.org to turn your goals into reality.

No responses yet