Categories
React

Making HTTP Requests with React Query — Mutations

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

Mutations

Mutations let us make HTTP requests to change data on a server by creating, updating, and deleting them.

For instance, we can write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useMutation } from "react-query";

export default function App() {
  const { mutate, isLoading, isError, isSuccess } = useMutation((data) =>
    axios.post("https://jsonplaceholder.typicode.com/posts", data)
  );

  return (
    <div>
      <div>{isLoading && "loading"}</div>
      <div>{isError && "error"}</div>
      <div>{isSuccess && "success"}</div>
      <button
        onClick={() => {
          mutate({
            title: "foo",
            body: "bar",
            userId: 1
          });
        }}
      >
        Add Todo
      </button>
    </div>
  );
}

We call the useMutation hook with a callback that lets us make a POST request to the API to submit some data.

The callback should return a promise.

The request payload is stored in the payload parameter.

The hook returns the mutate function that lets us make the request.

isLoading is true when the request is loading.

isError is true when a request fails.

isSuccess is true when the request is successfully completed.

Alternatively, we can replace isLoading , isSuccess , and isError with the status property:

import axios from "axios";
import React from "react";
import { useMutation } from "react-query";

export default function App() {
  const { mutate, status } = useMutation((data) =>
    axios.post("https://jsonplaceholder.typicode.com/posts", data)
  );

  return (
    <div>
      <div>{status === "loading" && "loading"}</div>
      <div>{status === "error" && "error"}</div>
      <div>{status === "success" && "success"}</div>
      <button
        onClick={() => {
          mutate({
            title: "foo",
            body: "bar",
            userId: 1
          });
        }}
      >
        Add Todo
      </button>
    </div>
  );
}

'loading' status is the status when the request is loading.

'error' status is the status when the request has an error.

'success' status is the status when the request is successful.

Resetting Mutation State

We can clear the mutation request state after the request is done.

To do this, we can call the reset method:

import axios from "axios";
import React, { useState } from "react";
import { useMutation } from "react-query";

export default function App() {
  const { reset, mutate } = useMutation((data) =>
    axios.post("https://jsonplaceholder.typicode.com/posts", data)
  );
  const [title, setTitle] = useState("");

  const onCreateTodo = (e) => {
    e.preventDefault();
    mutate({
      title
    });
  };

  return (
    <div>
      <form onSubmit={onCreateTodo}>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <br />
        <button type="submit">Create Todo</button>
        <button type="button" onClick={() => reset()}>
          reset
        </button>
      </form>
    </div>
  );
}

We call the reset method when we click on the reset the state returned by the useMutation hook.

Conclusion

We can make requests that change data on the server with the React Query useMutation hook.

Categories
React

Making HTTP Requests with React Query — Placeholder Data, Initial Data, and Prefetching

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

Placeholder Data

We can add placeholder data that are set when the request is loading.

To do this, we set the placeholderData property by writing:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"), {
    placeholderData: {}
  });

  return <div>{JSON.stringify(data)}</div>;
}

We can also load placeholder data from the cache.

To do this, we write:

import axios from "axios";
import React from "react";
import { useQuery, useQueryClient } from "react-query";
export default function App() {
  const queryClient = useQueryClient();
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"), {
    placeholderData: () => {
      return queryClient.getQueryData("yesNo");
    }
  });

  return <div>{JSON.stringify(data)}</div>;
}

We get the cached data with the queryClient.getQueryData method with the identifier of the request.

Initial Query Data

Also, we can set initial query data that are set when the query is first made.

To do this, we write:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"), {
    initialData: {}
  });

  return <div>{JSON.stringify(data)}</div>;
}

We can also set the amount of time in milliseconds that the data is considered fresh with the staleTime property.

To do this, we write:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"), {
    initialData: {},
    staleTime: 1000
  });

  return <div>{JSON.stringify(data)}</div>;
}

We set stateTime to 1000 milliseconds to invalidate the initial data after that time.

We can also set initialData to a function to only set the initial data when the query is first made:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"), {
    initialData: () => {
      return {};
    }
  });

  return <div>{JSON.stringify(data)}</div>;
}

Prefetching

React Query lets us prefetch data.

For instance, we can write:

import axios from "axios";
import React, { useEffect } from "react";
import { useQuery, useQueryClient } from "react-query";
export default function App() {
  const queryClient = useQueryClient();
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"));

  const prefetch = async () => {
    await queryClient.prefetchQuery("yesNo", () => Promise.resolve({}));
  };

  useEffect(() => {
    prefetch();
  }, []);

  return <div>{JSON.stringify(data)}</div>;
}

to call queryClient.prefetchQuery to prefetch the response for the request with identifier 'yesNo' .

Conclusion

We can prefetch data, set placeholder data, and set initial data for our requests with React Query.

Categories
React

Making HTTP Requests with React Query — Paginated and Infinite Scroll Queries

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

Paginated Queries

We can make paginated queries as we do with any other queries with the useQuery hook.

For instance, we can write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const [page, setPage] = React.useState(1);
  const { data: { data: { data, totalPages } } = { data: [] } } = useQuery(
    ["todo", page],
    ({ queryKey: [, page] }) => {
      return axios(
        `https://api.instantwebtools.net/v1/passenger?page=${page}&size=10`
      );
    }
  );

  return (
    <div>
      <button onClick={(p) => setPage(Math.max(1, page - 1))}>prev</button>
      <button onClick={(p) => setPage(Math.min(totalPages, page + 1))}>
        next
      </button>
      <div>
        {Array.isArray(data) &&
          data.map((d, i) => <p key={`${i}-${d.name}`}>{d.name}</p>)}
      </div>
    </div>
  );
}

We just create the page state and pass it into the request to make a paginated query.

We’ll see that there’s a lot of flashing since every query is going to make as a new query.

To make the queries without the extra flashing, we set the keepPreviousData option to true :

App.js

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const [page, setPage] = React.useState(1);
  const { data: { data: { data, totalPages } } = { data: [] } } = useQuery(
    ["todo", page],
    ({ queryKey: [, page] }) => {
      return axios(
        `https://api.instantwebtools.net/v1/passenger?page=${page}&size=10`
      );
    },
    {
      keepPreviousData: true
    }
  );

  return (
    <div>
      <button onClick={(p) => setPage(Math.max(1, page - 1))}>prev</button>
      <button onClick={(p) => setPage(Math.min(totalPages, page + 1))}>
        next
      </button>
      <div>
        {Array.isArray(data) &&
          data.map((d, i) => <p key={`${i}-${d.name}`}>{d.name}</p>)}
      </div>
    </div>
  );
}

Infinite Queries

We can also use React Query to make requests for infinite scrolling.

To do this, we use the useInfiniteQuery hook.

This lets us display all the items that have been fetched so far.

For instance, we can write:

import axios from "axios";
import React from "react";
import { useInfiniteQuery } from "react-query";
export default function App() {
  const [page, setPage] = React.useState(1);
  const { data, fetchNextPage } = useInfiniteQuery(
    "names",
    ({ pageParam = 1 }) => {
      return axios(
        `https://api.instantwebtools.net/v1/passenger?page=${pageParam}&size=10`
      );
    },
    {
      getNextPageParam: (lastPage) => {
        const { totalPages } = lastPage.data;
        return page < totalPages ? page + 1 : totalPages;
      }
    }
  );

  return (
    <div>
      <div>
        {data &&
          Array.isArray(data.pages) &&
          data?.pages.map((group, i) => {
            return group?.data?.data.map((d, i) => (
              <p key={`${i}-${d.name}`}>{d.name}</p>
            ));
          })}
      </div>
      <button
        onClick={() => {
          setPage(page + 1);
          fetchNextPage();
        }}
      >
        load more
      </button>
    </div>
  );
}

We call the useInfiniteQuery hook with the identifier for the request as the first argument.

The 2nd argument is the query function.

It takes an object with the pageParam property as the parameter.

pageParam is the return value of the getNextPageParam function.

getNextPageParam returns the next value of pageParam .

Then in the JSX, we return the data.pages array with the array of pages.

And we call map to render the data with the data from the page.

The load more button calls fetchNextPage to fetch the next page.

Conclusion

We can create paginated and requests for infinite scrolling with React Query.

Categories
React

Making HTTP Requests with React Query — Query Retries

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

Query Retries

The useQuery hook will retry requests automatically if they fail.

For instance, we can write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery(
    ["todo", 1],
    ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    },
    { retry: 3 }
  );

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

We set retry to 3 to retry the request 3 times if it fails.

Also, we can set retry to true to retry an infinite number of times.

And we can set retry to false to never retry.

Retry Delay

We can also set a retry delay with the retryDelay property.

The value should be in milliseconds.

For instance, we can write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000)
    }
  }
});

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery(["todo", 1], ({ queryKey: [, id] }) => {
    return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
  });

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

In index.js , we have the retryDelay method that takes the attemptIndex which is the attempt number being made minus 1.

And we return the number of milliseconds before another retry is made if a request fails.

Also, we can override the retry interval for a single request with the retryDelay property in the options we pass into the useQuery hook:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery(
    ["todo", 1],
    ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    },
    {
      retryDelay: 1000
    }
  );

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

Conclusion

We can set query retry interval easily with React Query.

Categories
React

Making HTTP Requests with React Query — Loading State and Refetching

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

Global Background Fetching Loading State

We can get the loading of all requests where any requests are loading.

To do this, we can use the useIsFetching hook.

For instance, we can write:

import axios from "axios";
import React from "react";
import { useQuery, useIsFetching } from "react-query";
export default function App() {
  const isFetching = useIsFetching();
  const { data } = useQuery(["todo", 1], ({ queryKey: [, id] }) => {
    return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
  });

  if (isFetching) {
    return "loading";
  }

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

to call the useIsFetching hook to check whether any requests in our app is loading.

Window Focus Refetching

By default, React Query will automatically request fresh data in the background when we focus on the window.

To disable this, we can set refrechOnWindowFocus to false when we create the QueryClient instance.

To do this for all requests, we write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false
    }
  }
});

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery, useIsFetching } from "react-query";
export default function App() {
  const isFetching = useIsFetching();
  const { data } = useQuery(["todo", 1], ({ queryKey: [, id] }) => {
    return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
  });

  if (isFetching) {
    return "loading";
  }

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

We can also disable this option per query by writing:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery, useIsFetching } from "react-query";
export default function App() {
  const isFetching = useIsFetching();
  const { data } = useQuery(
    ["todo", 1],
    ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    },
    { refetchOnWindowFocus: false }
  );

  if (isFetching) {
    return "loading";
  }

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

Disabling or Pausing Queries

We can disable a query from automatically running with the enabled property set to false .

For instance, we can write:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data, refetch } = useQuery(
    ["todo", 1],
    ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    },
    { enabled: false }
  );

  return (
    <div>
      <button onClick={() => refetch()}>Fetch Todo</button>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

We set enabled to false so that the request won’t be made when the component mounts.

To make the request, we can use the refetch function to make the request.

Conclusion

We can get the loading state of requests and control when requests are made with React Query.