Skip to content

Instantly share code, notes, and snippets.

@sahilrajput03
Last active April 6, 2024 11:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sahilrajput03/c870bee7bbc40cf303abc72cc245d14e to your computer and use it in GitHub Desktop.
Save sahilrajput03/c870bee7bbc40cf303abc72cc245d14e to your computer and use it in GitHub Desktop.
#react-query

Learn React Query

react-query is companies standard now

Date: 20 Aug, 2022, Souce: https://npmtrends.com/react-query

image

react-query is companies standard now

Date: 20 Aug, 2022, Souce: https://npmtrends.com/react-query

image

Using builtin abort controller in react-query

Click to open hd version of image

Want to do inifinite loading content?

Try awsome React Query's way of doing it: Click here

Config options:

{
      staleTime: 3000, //default is 0. Setting it to 3000 will tell react query to keep it fresh for 3 secs.
      //Also we can set staleTime to `Infinity` to query result will be fresh for life.
      //When we provide `initialData` it will be considered `fresh` for the time specified as staleTime.
      refetchOnWindowFocus: false, //default is true. Setting it false will tell react query to not refetch on window focus.
      cacheTime: 3000, // default is Infinity(never garbage collect). cacheTime tells react query to garabage collected(i.e., completely delete) in when the component using that query is unmounted(i.e., query result is no longer in use).
      //Also, set cacheTime to 0 if you want query result to dispose as soon query result becomes unused it gets removed immediately.
      enabled: 'someCondition or some variable here, so query is only fetched when its truthy value',
      //Also, setting `enabled` property to true (or atleast for once in past) will turn the queryInfo.isIdle flag to `true`.
      //enabled: flag is very important as it enables us to use queryInfo.isIdle flag to let us detemine if the query is not in loading state but in idle state, and we may want to show different things in ui in each of those states.
      retry: 1, // default is 3 times, i.e., if query fails it'll try 3 mroe times to fetch. Setting it to 1, will only try to fetch once more. You may set retry to `0` or `false` to tell react-query to not to retry at all.
      retryDelay: 300, //Will set dealy time between retries.
      initialData: 'someValueHere', // initialData will be used instead of query results, though it is stale by default so it'll be fetched from server on mount as well and will update the queryInfo.data .
      // initialStale: false, //This option is _DEPRECATED_ in react-query-v3(now initialData is always stale), and instead we have almost similar option(already studied) i.e., `staleTime` for the initial time it would be considered FRESHH and no actual query will be made for that time period.
      //const postsQuery = useQuery("posts", fetchPosts, {
      onSuccess: (data) => {
        increment();
      },
      onError: (error) => {
        console.log(); //Or we can do someting getting error from the request.
        },
      onSettled: (data, error) => {
      // if the reqeust was successful, error will be undefined.
      // if the reqeust was failed, data will be undefined.
    },
      refetchInterval: 1000,//Sets the time after each that interval the query will refetch and will be reflected back in component, yikes!!
      refetchIntervalInBackground: true,// default is false, so refetch will occur even if some other is opened currently.


  });

      
}

Important learnings

  1. queryInfo.isLoading (initially true) is false after first fetching-request promise is fulfilled(i.e., either resolved or rejected).

Sound features in react-queryv3 :

Retry mutations in same order.

Migrating to react query 3.

Component:

const Pokemon = () => {
  const queryInfo = usePokemon();
  log(queryInfo);
  return queryInfo.isLoading ? (
    'Loading...'
  ) : queryInfo.isError ? (
    queryInfo.error.message
  ) : (
    <div className="App">
      {queryInfo.isFetching ? 'Updating...' : null}
      <h1>Hello CodeSandbox</h1>
      {queryInfo.data.map((r) => (
        <div key={r.name}>{r.name}</div>
      ))}
    </div>
  );
};

Dev tools:

Remember to place ReactQueryDevtools component under component tree of , so that devtools can access it.

import { ReactQueryDevtools, ReactQueryDevtoolsPanel } from "react-query/devtools";
<ReactQueryDevtools/> // This is beatiful embedded mode.
<ReactQueryDevtoolsPanel /> //This one is for embedded mode.

Tip: Positioning devtools

<ReactQueryDevtools position="bottom-right" />

From api:      
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right"
Defaults to bottom-left

src

Others

(Query cancellation - vid 17 )Cancelling request via axios+react-query instead of using debounce(though using debounce is much cleaner option IMO )

Using axios

import axios, {CancelToken} from "axios";

const myAxiosRequest = () => {
      const source = CancelToken.source()
      
      const promise = new Promise(resolve => setTimeout(resolve, 1000))
        .then(() => {
          return axios.get(
            `https://pokeapi.co/api/v2/pokemon/${pokemon}`,
            {
              cancelToken: source.token
            }
          )
        })
        .then(res => res.data)
        
        promise.cancel = () => {
          source.cancel('Query wass cancelled by React Query')
        }
        
        return promise
    }

function PokemonSearch({pokemon}){
  const queryInfo = useQuery(
    ['pokemon', pokemon], myAxiosRequest)
  return ...
}

Using fetch

const myFetchRequest = () => {
      const controller = new AbortController()
      const signal = controller.signal
      const promise = new Promise(resolve => setTimeout(resolve, 1000))
        .then(() => {
          return fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}`, {
            method: 'get',
            signal,
          })
        })
        .then(res => res.json())
        
        promise.cancel = () => {
            controller.abort()
        }
        
        return promise
    }

function PokemonSearch({pokeomn}){
  const queryInfo = useQuery(
    ['pokemon', pokemon], myFetchRequest)
}

Invalidating queries

import {queryClient} from "./QueryClient";
//src: vid 28.
//****Also, we have used `staleTime: Infinity` for the "number" query so it'll not automatically refetch query on window refocus, but when we invalidate query with queryClient.invalidateQueries method, it's gonna refetch immediately. Yikes!!
<button
  onClick={() => {
  queryClient.invalidateQueries("number",{
            refetchActive: false, //Default is true. Setting it false make it refetch only on window refocus or via refetch function only.
            refetchInactive: true, //Default is false. Setting it to true will refetch the inactive queries too.
      });
  }}>
    Invalidate number query
</button>

Quoting from docs :

If you do not want active queries to refetch, and simply be marked as invalid, you can use the refetchActive: false option.

src: Link.

Prefetch query with with either on component mount or onMouseEnter event on html elements

import {queryClient} from "./QueryClient";
queryClient.prefetchQuery(['post', post.id], () => fetchPost(post.id), null, {force: true})
//Use above statement to prefetchQuery according to your usecase.
//Also, `force` option is truly optional as with force option, react-query will refetch fresh queries(for e.g., for Post Component => staleTime: 60 * 1000), otherwise it would not refetch fresh queries. Yikes!!
//

Put vs. Patch

Put: PUT is a method of modifying resource where the client sends data that updates the entire resource.

Patch: Unlike PUT, PATCH applies a partial update to the resource.

Tanner linsley suggests to use 'queryClient.setQueryData' followed by 'queryClient.invalidateQuery' (vid 38)

Its bcoz, we must be sure that the data(partial data) we are manually settting for a query via setQueryData method could be completely updated via the query request followed by invalidateQuery method.

Accessing queryClient

You may use either way of using queryClient in your app, i.e.,

import {queryClient} from './queryClient.js'
//Yup this works great!!
// or if you wanna follow the docs way.., then 
import {useQueryClient} from './react-query'

const App = () => {
  const queryClient = useQueryClient()
  //Thought this is they way to use queryClient as mentioned in docs, but the former one works in equally decent way. Yikes!!
}

Src: [Link in docs](https://react-query.tanstack.com/guides/query-invalidation#query-matching-with-invalidatequeries)

### Using axios's params option

```js
const fetchPosts = () => {
  axios.get('/api/posts', {
    params: {
      pageSize: 10,
      pageOffset: page,
    }
  })
}

usePaginatedQuery() has been deprecated in favor of the keepPreviousData option

The new keepPreviousData options is available for both useQuery and useInfiniteQuery and will have the same "lagging" effect on your data:

import { useQuery } from 'react-query'
 
 function Page({ page }) {
   const { data } = useQuery(['page', page], fetchPage, {
     keepPreviousData: true,
   })
 }

src

Video 43

queryInfo.resolvedData and queryInfo.latestData are deprecated, there's no such things in latest version of react-query, but queryInfo.data.

Video 45

The content of vid 45 is mostly(80%) deprecated, so instead review only the tutorial's code in reactProjects/learning-react-query folder only. Also, see the amazing docs exaplaining other great things about infinite query api here.

Vid 46 and 47 talk about amazing next's getServerStaticProps api from nextjs and fits really well with react-query, yikes!!

Cool..., Read more at nextjs docs. Link 1, and Link 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment