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.

Categories
React

Making HTTP Requests with React Query — Parallel and Dependent 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.

Parallel Queries

Sometimes, we may want to make multiple GET requests concurrently.

To do this, we can add multiple useQuery hooks:

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({
    queryKey: ["todo", 1],
    queryFn: ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    }
  });
  const { data: yesNoData } = useQuery("yesNo", () =>
    axios("https://yesno.wtf/api")
  );

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

We have useQuery hooks to make 2 requests in parallel.

Dynamic Parallel Queries with useQueries Hook

We can add dynamic parallel queries with the useQueries hook.

It returns an array of query results.

For instance, we can write:

import axios from "axios";
import React from "react";
import { useQueries } from "react-query";
export default function App() {
  const todos = useQueries(
    Array(5)
      .fill()
      .map((_, i) => i + 1)
      .map((id) => {
        return {
          queryKey: ["todo", id],
          queryFn: ({ queryKey: [, id] }) => {
            return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
          }
        };
      })
  );

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

We call useQueries with an array of query objects.

We call map to map the array of numbers we created with:

Array(5)
  .fill()
  .map((_, i) => i + 1)

to query objects.

And todos is an array of query objects.

Dependent Queries

We can make also make multiple HTTP requests where one depends on the other.

To do this, we write:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data: id } = useQuery("id", () => Promise.resolve(1));

  const { data } = useQuery(
    ["todo", id],
    ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    },
    {
      enabled: Boolean(id)
    }
  );

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

to make one query after the other.

We call the first useQuery hook to get an id for our todo.

Then we call useQuery again to pass in the id to the callback.

We set the enabled property to a boolean expression to indicate when we want to query to run.

We set it so that the 2nd query is run when id is truthy, which should only happen when we get the id from the first useQuery hook.

Conclusion

We can make parallel or sequential queries with React Query easily.

Categories
React

Making HTTP Requests with React Query — Query Functions

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 Functions

Query functions are functions that returns a promise and it’s passed into the 2nd argument of 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 { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"));

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

to return the promise return by the axios function.

The returned promises’ data will be set as the value of the data property.

Handling and Throwing Errors

React Query expects that an error is thrown in the query function when an error occurred.

If it doesn’t then we’ve to throw it ourselves.

For instance, we can write:

import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { error } = useQuery("todo", async () => {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/posts/100000000000"
    );
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    return response.json();
  });

  return <div>{error && error.message}</div>;
}

If we use the Fetch API to make HTTP requests, then we’ve to check whether the response.ok property is true .

If it’s not, then we’ve to throw an error to let React Query know that an error has occurred.

This has to be done since fetch doesn’t throw an error when we get a non-200 series response.

If response.ok is true , we return the response by calling response.json() .

Query Object

We can pass in a query object instead of pass in every as separate arguments to make our GET request.

For instance, we can write:

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

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

queryKey has the identifier of the request.

queryFn is the function for making the request.

Conclusion

We can pass in functions that returns a promise to make requests to the useQuery hook to make GET requests with React.