1

I'm trying to use React-Query with Astro to fetch data from my Django Rest Framework backend. Astro has been a great way to organize my react-based frontend but I am worried it might not be compatible with React-Query.

Whenever I try to make a query to my backend I get an 'isLoading' value of true (and an isError of false). I never manage to recover the data from my endpoints however.

I have been following a variety of tutorials with the same results. Here is the code where I'm stuck:

import { QueryClient, useQueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { gettestApi } from "../../api/testApi";

function MyComponent(props) {
    const queryClient = useQueryClient();
    const {
        isLoading,
        isError,
        error,
        data: test
    } = useQuery('test', gettestApi)

    let content
    if (isLoading) {
        content = <p>Loading...</p>
    } else if (isError){
        content = <p>{error.message}</p>
    } else {
        content = JSON.stringify(test)
    }

As you can see, I import an axios function from /api/testAPI.js which looks like this:

import axios from "axios"

const testApi = axios.create({
    baseURL: "http://127.0.0.1:8000"
})

export const gettestApi = async () => {
    return await testApi.get("/api/endpoint/").then(response => response.data)
}

That's how most tutorials I have seen and the official documentation wrap up their examples, however my backend server which should be triggered by this endpoint records absolutely no hits from react-query, which is curious to me. I understand that nothing 'calls' my react-query or my gettestApi() function, but it seems to be unnecessary for other people to retrieve their data.

Maybe it would be useful to point out that contrary to other framework with React, Astro does not have an App.js root to surround with

<QueryClientProvider client={client}>
<App />
</QueryClientProvider>

Instead, I have added these QueryClientProvider brackets to the highest React component I could.

I feel like I'm missing some intuition about Tanstack Query/ React-Query. Could anybody point me in the right direction? Thanks a lot for the help.

chenard612
  • 101
  • 2
  • 15

3 Answers3

1

From what I've seen in the astro docs:

The most important thing to know about Astro components is that they render to HTML during your build. Even if you run JavaScript code inside of your components, it will all run ahead of time, stripped from the final page that you send to your users. The result is a faster site, with zero JavaScript footprint added by default.

So it seems all react code only runs on the server, where data fetching via useEffect or useSyncExternalStore subscriptions just doesn't run. But this is exactly what react-query is doing, so yeah I think they don't work well together. I'm also not sure what the purpose of react-query in a setting would be where there is no client side javascript.

TkDodo
  • 20,449
  • 3
  • 50
  • 65
1

You can set client:only on your Astro component so the React component doesn't run on the server. There are shared-state limitations but still React Query feels better than just fetch + useEffect + own-code even if its not in a complete React app. In this example I'm also using an init function that reads cookies from the client's browser which is another case for when to use client:only.

Astro:

---
import Layout from "../../layouts/Layout.astro";
import ClientPanel from "../../components/client/ClientPanel";
---
<Layout title={ 'Client' }>
    <ClientPanel client:only></ClientPanel>
</Layout>

React:

// imports

const queryClient = new QueryClient()

/** client:only component */
const ClientPanel = () => (
    <QueryClientProvider client={queryClient}>
      <ClientData />
    </QueryClientProvider>
)

const ClientData = () => {
    
    const { getUser, getSession } = useSession(); // read cookies functions
    const [ user, setUser ] = useState(getUser);

    const { isLoading, error, data } = useQuery({
        queryKey: ['patientData'],
        queryFn: () => getSession() // validate or refresh token
        .then(session => fetchPatientData(session.tokens.token))
        .catch(error => error === 'INVALID_SESSION' ? null : undefined)
    })

    if (!user || data === null) window.location.replace('/login')
    
    // return statement, etc.
0

You can create an HOC that can return a new component that has a QueryClientProvider wrapper on the existing component that's using react-query

HOC:

    export const QueryProviderHoc = (Comp: React.FC) => {
       const newComp = () => {
        return (
            // this is your QueryClientProvider
            <QueryProvider>
               // Component that is using react-query
                <Comp />
            </QueryProvider>
        )
    }

    return newComp
  }

Component with react-query:

const Items = () => {
    // some custom react-query hook
    const {data, isLoading} = useGetItems()

    if(!data || isLoading)
    {
        return <p className="text-center">Loading...</p>
    }
    return (
        <>
            <ItemList  data={data}/>
        </>
    )
}


export default QueryProviderHoc(Items)

On Astro Page/file:

<Layout title="dashboard">
    <p>Hello world</p>

    <Items client:load />
</Layout>
schierke
  • 1
  • 1