# React Query

## Introduction

* The library to handle data fetching, including retry, caching, error handling, so as to reduce the boilerplate code
* Convenient to create custom hook to fetching or updating data in order to achieve separate of concern

## Fresh & Stale State

* Fresh: all the data must come from cache, no background refetching will be happened
* Stale: data can still come from cache, but the background refetching can be happened

## Default Behavior

* Stale queries are refetched automatically in the background when:
  * New instances of the query mount
  * The window is refocused
  * The network is reconnected.
  * The query is optionally configured with a refetch interval.
* If Query fails, will silently retry 3 times
* The data of the inactive queries will be cached for 5 mins
* The staleTime is 0
* `isLoading`: Your query has no data and is currently *loading* for the first time
* `isFetching` flag is not part of the internal state machine - it is an additional flag that will be true whenever a request is in-flight. You can be fetching and success, you can be fetching and error - but you cannot be loading and success at the same time.
* React Query will attempt to compare the old state and the new and keep as much of the previous state as possible to prevent trivial re-rendering

{% embed url="<https://tkdodo.eu/blog/react-query-render-optimizations>" %}

{% embed url="<https://tkdodo.eu/blog/status-checks-in-react-query>" %}

{% embed url="<https://tkdodo.eu/blog/react-query-as-a-state-manager>" %}

## Query Cancellation

* Queries that unmount or become unused  will be cancelled if you consume the AbortSignal or attach a cancel function to your Promise, the Promise will be cancelled (e.g. aborting the fetch)&#x20;

```typescript
import axios from 'axios'

const query = useQuery('todos', ({ signal }) =>
  axios.get('/todos', {
    // Pass the signal to `axios`
    signal,
  })
)
```

```typescript
import axios from 'axios'

const query = useQuery('todos', () => {
  // Create a new CancelToken source for this request
  const CancelToken = axios.CancelToken
  const source = CancelToken.source()

  const promise = axios.get('/todos', {
    // Pass the source token to your request
    cancelToken: source.token,
  })

  // Cancel the request if React Query calls the `promise.cancel` method
  promise.cancel = () => {
    source.cancel('Query was cancelled by React Query')
  }

  return promise
})
```

* The query can also be cancelled manually

```typescript
const [queryKey] = useState('todos')

const query = useQuery(queryKey, async ({ signal }) => {
  const resp = await fetch('/todos', { signal })
  return resp.json()
})

const queryClient = useQueryClient()

return (
  <button onClick={(e) => {
    e.preventDefault()
    queryClient.cancelQueries(queryKey)
   }}>Cancel</button>
)
```

{% embed url="<https://tanstack.com/query/v3/docs/framework/react/guides/query-cancellation>" %}

## Setup Provider

* To set up the provider for children, so that react-query can be used in component
* To customize the default setting for library

```typescript
import axios from "axios";
import { ReactNode } from "react";
import {
  DefaultOptions,
  QueryClient,
  QueryClientProvider as Provider,
} from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";

const defaultOptions: DefaultOptions = {
  queries: {
    refetchOnReconnect: false,
    refetchOnWindowFocus: false,
    retry: (failureCount, error) =>
      failureCount < 3 &&
      !(axios.isAxiosError(error) && error?.response?.status === 401),
  },
};

const queryClient = new QueryClient({ defaultOptions });

interface QueryClientProviderProps {
  children: ReactNode;
}

export default function QueryClientProvider({
  children,
}: QueryClientProviderProps) {
  return (
    <Provider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </Provider>
  );
}
```

```typescript
export default function App(){
    <QueryClientProvider>
        {children}
    </QueryClientProvider>
}
```

## UseQuery

* Handle fetching data when the page is mounted and the dependencies are changed
* Data will be cached based on the query key

```typescript
interface PageListApiResponse {
  results: Page[];
  maxPublishedPages: number;
}

export default function usePageListQuery(
  state: PageState,
  offset: number,
  options: UseQueryOptions<PageListApiResponse, AxiosError<any>> = {}
) {
  const axios = useContext(AxiosContext);

  return useQuery({
    queryKey: ["pageList", offset, state],
    queryFn: async ({ queryKey }) => {
      const { data } = await axios.get<PageListApiResponse>(
        `${process.env.REACT_APP_API_URL}/pages?_limit=10&_start=${queryKey[1]}&state=${queryKey[2]}`
      );
      return data;
    },
    ...options,
  });
}
```

```typescript
const {
    data: { results: listPagesData = [], maxPublishedPages = 0 } = {},
    isLoading,
    isFetching,
    refetch,
  } = usePageListQuery(listingStatus, pageOffset, {
    onSuccess:(data)=> {...},
    onError: (err)=> {...},
    enabled: allCountData && allCountData.count > 0,
    select: ({ results, ...data }) => ({
      ...data,
      results: [...results].sort((a, b) => {
        if (a.url === homePageSettings?.home) return -1;
        if (b.url === homePageSettings?.home) return 1;
        return 0;
      }),
    }),
  });
```

{% embed url="<https://tkdodo.eu/blog/react-query-and-type-script>" %}

{% embed url="<https://tkdodo.eu/blog/effective-react-query-keys>" %}

## UseMutation

* Used to update data

```typescript
export default function usePageUpdateMutation({
  onSuccess,
  ...options
}: MutationOptions<AxiosResponse<Page>, AxiosError<any>, Page> = {}) {
  const axios = useContext(AxiosContext);
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (page: Page) =>
      axios.put<Page>(
        `${process.env.REACT_APP_API_URL}/pages/${page.id}`,
        page
      ),
    onSuccess: (data, variables, context) => {
      if (onSuccess) onSuccess(data, variables, context);
      // to clear the query, 
      // so that the query will be loaded again and trigger isLoading
      queryClient.invalidateQueries("allPageList");
      queryClient.invalidateQueries(["page", variables.id]);
      // set the data to the existing query directly
      queryClient.setQueryData(['test', variables.id], data)
    },
    ...options,
  });
}
```

{% embed url="<https://tkdodo.eu/blog/mastering-mutations-in-react-query>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://petercheng7788.gitbook.io/developer-note/frontend/react/data-fetching/react-query.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
