Been stuck on this issue for some time now trying to retro fit react-query in to my codebase.
I have a working version using getStaticProps.
I have been experimenting with router.isReady from next/router to try and force the page to wait for the router.query value to be present before passing it as props to the react hook.
Here is my code…
pages/artist/[artistId].js
import React, { useEffect, useState } from "react"; import { useSession } from "next-auth/react"; import { useRouter } from "next/router"; import { useQuery } from "@tanstack/react-query"; import Head from "next/head"; import ArtistHeader from "../../components/ArtistHeader"; import UploadButton from "../../components/UploadButton"; import fetchArtist from "../../hooks/getArtists"; import styles from "../../styles/artistPage.module.css"; export default function Artist() { const { status } = useSession(); const [artist, setArtist] = useState(null); const router = useRouter(); const artistId = router.query.artistId; useEffect(() => { if (status === "unauthenticated") { router.push("/auth/signin"); } }, [status, router]); //check if router isReady useEffect(() => { if (!router.isReady) { return; } }, [router.isReady]); const { data, status: artistStatus } = useQuery( ["artist"], () => fetchArtist(artistId), { staleTime: 1000 * 60 * 60 * 24, cacheTime: 1000 * 60 * 60 * 24, } ); useEffect(() => { if (artistStatus === "success") { setArtist(data); } }, [artistStatus, data]); return ( <section className={styles.wrapper}> {/* <Head> <title>{artist.screenName}</title> </Head> <div className={styles.artistPage}> <ArtistHeader artist={artist} /> <div className={styles.songContainer}> <UploadButton /> </div> </div> */} </section> ); }
Here is the firestore data fetch…
hooks/getArtists.js
import { getDoc, doc } from "firebase/firestore"; import { db } from "../pages/api/auth/firebase/config"; export default async function fetchArtist(artistId) { console.log(artistId); const artistRef = doc(db, "users", artistId); const artistDoc = await getDoc(artistRef); if (artistDoc.exists()) { return artistDoc.data(); } else { // doc.data() will be undefined in this case console.log("No such document!"); } }
I really want my data fetching to be separate and handled by react-query. Any ideas wwould be much appreciated.
Thanks in advance!
Advertisement
Answer
You don’t really need an useEffect
just to check if router.isReady
, and you also don’t need the artist
state since it’s redundant, react-query takes care of this for you.
What you can do is disable the query using the enabled
option of useQuery
if artistId
is undefined
, that way you are preventing the request to be made on first render when the query
object is empty.
const { data: artist, status: artistStatus } = useQuery( ["artist"], () => fetchArtist(artistId), { staleTime: 1000 * 60 * 60 * 24, cacheTime: 1000 * 60 * 60 * 24, enabled: Boolean(artistId) // or directly check router.isReady flag } );
Note: make sure to use optional chaining operator when trying to access artist
object’s properties since it will be undefined
on first render.
Reference to useQuery’s enabled option: https://tanstack.com/query/v4/docs/guides/disabling-queries