Benjamin Higginbotham
Benjamin Higginbotham

Reputation: 261

React-Query useQuery sending infinite fetch requests

When I use useQuery as I have below, in the manner it is used on react-query's documentation, it sends an infinite number of fetch requests. The documentation also said that fetch doesn't automatically throw an error. So, I tried the same code below but with getAlbums using a try-catch block. I've also tried to configure the staleTime in an object in the useQuery hook, but it didn't work and I really don't know what stale time even is. LOL. Help me out please. Trying to get this project done by Monday

found this error in my console:

(node:27609) UnhandledPromiseRejectionWarning: ReferenceError: navigator is not defined
    at OnlineManager.isOnline (/Users/benridesbikes/repos/photo_album/node_modules/react-query/lib/core/onlineManager.js:64:5)
    at /Users/benridesbikes/repos/photo_album/node_modules/react-query/lib/core/retryer.js:142:86
(Use `node --trace-warnings ...` to show where the warning was created)
(node:27609) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:27609) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
import React from "react";
import { useQuery, useMutation, useQueryClient } from "react-query";
import Link from "next/link";
import Form from "../../components/styles/Form";
import Container from "../../components/styles/AlbumsIndex";
import Button from "../../components/styles/Button";

async function getAlbums() {
  const response = await fetch(`api/albums/`);
  const { albums } = await response.json();
  return albums;
}

async function createAlbum(newAlbum) {
  const response = await fetch(`/api/albums/create`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(newAlbum),
  });
  const { album } = await response.json();
  return album;
}

async function deleteAlbum(albumId) {
  await fetch(`/api/albums/delete`, {
    method: "DELETE",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(albumId),
  });
}

export default function Index() {
  const queryClient = useQueryClient();

  const refetchQuery = async () => {
    await queryClient.refetchQueries();
  };

  const { data: albums, error } = useQuery("albums", getAlbums);

  const mutationCreateAlbum = useMutation(createAlbum, {
    onSuccess: refetchQuery(),
  });

  const mutationDeleteAlbum = useMutation(deleteAlbum, {
    onSuccess: refetchQuery(),
  });

  const [formData, setFormData] = React.useState({
    name: "",
    description: "",
  });

  const handleChange = (event) => {
    setFormData({ ...formData, [event.target.name]: event.target.value });
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    mutationCreateAlbum.mutate({
      name: formData.name,
      description: formData.description,
    });
    setFormData({
      name: "",
      description: "",
    });
  };

  const useDeleteMutation = (albumId) => {
    mutationDeleteAlbum.mutate({
      id: albumId,
    });
  };

  return (
    <Container>
      <Form>
        <h1>Create a New Album</h1>
        <label htmlFor="name">
          Name:
          <input
            type="text"
            id="name"
            name="name"
            value={formData.name}
            onChange={handleChange}
            placeholder="Give Your Album a Name!"
          />
        </label>
        <label htmlFor="description">
          Description:
          <input
            type="text"
            id="description"
            name="description"
            value={formData.description}
            onChange={handleChange}
            placeholder="Give Your Album a Description!"
          />
        </label>
        <Button onClick={(event) => handleSubmit(event)}>
          Make New Album!
        </Button>
      </Form>
      <div className="albums">
        {albums &&
          albums.map((album) => (
            <div className="album" key={album.id}>
              <Link href={`/albums/${album.id}`}>
                <a>
                  <Button>{album.name}</Button>
                </a>
              </Link>
              <h3>{album.description}</h3>
              <Button onClick={() => useDeleteMutation(album.id)}>
                Delete
              </Button>
            </div>
          ))}
      </div>
    </Container>
  );
}

Upvotes: 0

Views: 3167

Answers (2)

TkDodo
TkDodo

Reputation: 28753

The issue seems to be that you call the refetchQuery function when you declare the mutation:

  const mutationCreateAlbum = useMutation(createAlbum, {
    onSuccess: refetchQuery(),
  });

refetchQuery() is a direct function invocation. What you want is either:

  const mutationCreateAlbum = useMutation(createAlbum, {
    onSuccess: refetchQuery,
  });

note the missing call parenthesis, so we are just passing the function, but not invoking it. Or:

  const mutationCreateAlbum = useMutation(createAlbum, {
    onSuccess: () => refetchQuery(),
  });

which declares a new inline function and then calls refetchQuery.

Upvotes: 2

Benjamin Higginbotham
Benjamin Higginbotham

Reputation: 261

Solved! IDK exactly why this solution works. I have a hunch it has to do with hooks and React rerendering. In short the function:

 const refetchQuery = async () => {
    await queryClient.refetchQueries();
  };

is what keeps sending the fetch over and over again. The solution is to delete this function and instead call 'queryClient.refetchQueries()' as an async function after onSuccess, like so:

  const queryClient = useQueryClient();

  const { data: albums, error } = useQuery("albums", getAlbums);

  const mutationCreateAlbum = useMutation(createAlbum, {
    onSuccess: async () => await queryClient.refetchQueries(),
  });

  const mutationDeleteAlbum = useMutation(deleteAlbum, {
    onSuccess: async () => await queryClient.refetchQueries(),
  });

Upvotes: 0

Related Questions