I’m working on a React application where I use react-router for routing and react-query for data management. My goal is to fetch data within the react-router loader function, but I’m encountering issues with accessing isLoading and isError states from react-query.
API Query and Hook Definition:
import { useQuery, UseQueryResult } from '@tanstack/react-query'
import { getDefaultValues } from './apiClient'
import { AxiosError } from 'axios'
export const useFetchDefaultValues = () => {
return useQuery({
queryKey: ['defaultValues'],
queryFn: () => getDefaultValues(),
})
}
export const getDefaultValues = async () => {
try {
const { data } = await axios.get(`${baseUrl}/pulse/getDefaultValues`)
return data
} catch (error) {
console.log(error)
throw new Error('Error fetching default values')
}
}
React Router Loader Function:
export const loader: LoaderFunction = async () => {
const { data: defaultData } = await useFetchDefaultValues()
return defaultData ? defaultData.results : []
}
import BreadCrumb from '@/components/BreadCrumb'
import DataTable from '@/components/DataGrid/DataTable'
import { fetchFulfilled } from '@/features/defaultValues/defaultValuesSlice'
import { useEffect } from 'react'
import { useAppDispatch } from '@/Hooks/hooks'
import { useLoaderData } from 'react-router-dom'
const ProgramOverviewPage = () => {
const defaultData = useLoaderData() as Parameter[]
const dispatch = useAppDispatch()
useEffect(() => {
if (defaultData) {
dispatch(fetchFulfilled(defaultData))
}
}, [defaultData, dispatch])
return (
<div className='flex flex-col gap-4 h-full'>
<div className=''>
<BreadCrumb page='T_Program_Overview' />
</div>
<div className='flex-1 p-6 overflow-hidden bg-background rounded-xl'>
<DataTable />
</div>
</div>
)
}
export default ProgramOverviewPage
Problem:
When trying to use the useFetchDefaultValues hook inside the loader, I receive the following error:
Unexpected Application Error!
Cannot read properties of null (reading 'useContext')
TypeError: Cannot read properties of null (reading 'useContext')
at Object.useContext (http://.../react-query.js:1062:29)
...
It seems that using react-query hooks within the loader is not working because the loader runs in a different context than React components.
Direct API Fetch in Loader (which works but lacks loading/error state handling):
import { defer, LoaderFunction } from 'react-router-dom'
import { getDefaultValues } from '@/api/apiClient'
export const loader: LoaderFunction = async () => {
const defaultDataPromise = getDefaultValues()
return defer({ defaultData: defaultDataPromise })
}
Problem: When fetching the API directly in the loader, I successfully get the data, but I don’t have access to isLoading, isError, or other React Query hook properties to manage the UI state effectively.
Questions:
How can I correctly use react-query within a react-router loader while avoiding useContext errors?
Is there a recommended way to handle loading and error states when performing API queries in the loader function?
If using react-query in the loader is not feasible, how can I handle loading and error states effectively when fetching data directly in the loader?
Thank you in advance for your help!
2
You can’t access hooks outside of React Components or custom hooks. Those are the rules of hooks by react. Hooks also aren’t async, so you cannot simply await
a hook.
I have a blogpost for integrating react-router with react-query, what you’d want to do is get access to the queryClient
and call ensureQueryData
there:
export const loader =
(queryClient) =>
async () => {
return queryClient.ensureQueryData({
queryKey: ['defaultValues'],
queryFn: () => getDefaultValues(),
})
}