Skip to content
Advertisement

Should I use router.isReady when fetching with react-query?

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

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement