React – How to use react-query?

How to install & configure

yarn add react-query
const RootComponent = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        refetchOnWindowFocus: true, // React Query automatically requests fresh data for you in the background
      },
    },
  })

  return (<QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>)
}

ReactDOM.render(<RootComponent />, document.getElementById('root'))

Add devtools

By default, React Query Devtools are not included in production bundles when process.env.NODE_ENV === 'production', so you don’t need to worry about excluding them during a production build.

...

import { ReactQueryDevtools } from 'react-query/devtools'

const Bootstrap = () => {
  ...
  return (
        ...
        <App />
        <ReactQueryDevtools initialIsOpen={false} />
        ...
  )
}

ReactDOM.render(<RootComponent />, document.getElementById('root'))

How to request Rest API?

import { useQuery } from 'react-query';
import axios from 'axios';

export const fetchPosts = async () => {
  const { data } = await axios.get(
    'https://jsonplaceholder.typicode.com/comments?postId=5'
  );
  return data;
};

const useFetchPosts = () => useQuery(['fetchPosts'], () => fetchPosts());

export default useFetchPosts;
const Home: FC<Props> = ({ navigation }) => {
  const { data, isLoading, isSuccess } = useFetchPosts();

  if (isLoading) {
    console.log('=========isLoading', isLoading);
  }

  if (data) {
    console.log('=========data', data);
  }

  if (isSuccess) {
    console.log('=========isSuccess', isSuccess);
  }

  return (...)
});

How to request graphQL?

Install

yarn add graphql graphql-request

Usage 1

// queries.js

import { gql } from 'graphql-request'

export const ONE_GRAPHQL_QUERY = gql`
  query getPost($postId: ID!) {
    getPost(_id: $postId) {
      _id
      content
      description
      title
    }
  }
`
const graphQLClient = new GraphQLClient('https://graphql.abcd.com/', {
  method: 'POST',
  headers: {
    'x-api-key': xxxxxxxxxx,
    'content-type': 'application/json',
  },
})

/////////////////////
/////////////////////

export const fetchPost = async (postId) => {
  const response = await graphQLClient.request(ONE_GRAPHQL_QUERY, {
    id: postId,
  })
  return response
}
const Demo = ({ postId }) => {
  const { data, isLoading, error } = useQuery(
    ['fetchPost', postId],
    () => fetchPost(postId)
  )

  if (data) {
    console.log(data)
  }

  return (
      <div>{isLoading ? 'Loading...' : 'Done!'}</div>
  )
}

export default Demo

How to trigger a fetch request with a button?

  • Use refetch()
const { refetch: fetchSomething } = useQuery(
  ['fetchMyData', input],
  () => fetchMyData(input),
  {
    enabled: false,
  }
)

function onClick(e) {
  fetchSomething()
}

How to trigger create/ update/ delete data with a button?

  • Method 1
import { GraphQLClient } from 'graphql-request'
import { useMutation } from 'react-query'

const graphQLClient = new GraphQLClient(
  process.env.API_GRAPHQL_ENDPOINT,
  {
    method: 'GET',
    headers: {
      Authorization: accessToken,
    },
  }
)

const updateUser = async (input) => {
  const response = await graphQLClient.request(UPDATE_USER_QUERY, {
    input,
  })
  return response
}

export const useUpdateUser = () => {
  return useMutation(
    async (data) => {
      return await updateUser(data)
    },
    {
      onMutate: (editedValue) => {
        // console.log(data, error, editedValue)
      },
      onError: (error, editedValue) => {
        console.log(error, editedValue)
      },
      onSettled: (data, error, editedValue) => {
        // console.log(data, error, editedValue)
      },
      onSuccess: (data, variables) => {
        // console.log(data, variables)
      },
    }
  )
}
// Use it in your component

const {
  mutateAsync: updateUser,
  status: userUpdateStatus,
  isLoading: isLoadingUserUpdate,
  data: userUpdateData,
  error: userUpdateErr
} = useUpdateNote()

function onClickSaveUser() {
  const detail = getUserDetail(...)
  updateUser(detail)
}

graphql-hasura-with-react-query-mutation

  • Method 2
export const updateUserNote = async (input) => {
  const response = await graphQLClient.request(UPDATE_USER_QUERY, {
    input,
  })
  return response
}
const mutation = useMutation(async (note) => {
  return await updateBlackbookNote(note)
})

function onClickSaveUser() {
  const detail = getUserDetail(...)
  mutation.mutate(detail)
}

react-query.tanstack.com/guides/mutations

How to save fetched data into useState?

const YourComponent = () => {
  const [userList, setUserList] = useState(null)

  const { isLoading } = useQuery(
    ['fetchUserList'],
    () => fetchUserList(),
    {
      onSuccess: (data) => {
        setUserList(data)
      },
    }
  )

...

How to request paginated queries?

function Todos() {
   const [nextPageParam, setNextPageParam] = React.useState(0)
 
   const fetchProjects = (nextPageParam = 0) => fetch('/api/projects?page=' + nextPageParam).then((res) => res.json())
 
   const {
     isLoading,
     isError,
     error,
     data,
     isFetching,
     isPreviousData,
   } = useQuery(['projects', nextPageParam], () => fetchProjects(nextPageParam), { keepPreviousData : true })
 
   return (
     <div>
       {isLoading ? (
         <div>Loading...</div>
       ) : isError ? (
         <div>Error: {error.message}</div>
       ) : (
         <div>
           {data.projects.map(project => (
             <p key={project.id}>{project.name}</p>
           ))}
         </div>
       )}

       {/* Pages */}
       <span>Current Page: {nextPageParam + 1}</span>
       <button
         onClick={() => setNextPageParam(old => Math.max(old - 1, 0))}
         disabled={nextPageParam === 0}
       >
         Previous Page
       </button>{' '}
       <button
         onClick={() => {
           if (!isPreviousData && data.hasMore) {
             setNextPageParam(old => old + 1)
           }
         }}
         // Disable the Next Page button until we know a next page is available
         disabled={isPreviousData || !data?.hasMore}
       >
         Next Page
       </button>
       {isFetching ? <span> Loading...</span> : null}{' '}
     </div>
   )
 }

How to request infinite queries?

import { useInfiniteQuery } from 'react-query'

const fetchProjects = (customParam, queryParam) =>
  fetch('/api/projects?cursor=' + queryParam + '?category=' + customParam)

 
function Projects({ customParam }) { 
   const {
     data,
     error,
     fetchNextPage,
     hasNextPage,
     isFetching,
     isFetchingNextPage,
     status,
   } = useInfiniteQuery('projects', ({ pageParam: queryParam }) => { 

     // "pageParam" is returned from the "getNextParam"
     return fetchProjects(customParam, queryParam)
   },
   {
     getNextPageParam: (lastPage, pages) => {
       const queryParam = lastPage.nextCursor
       
       // If no key is returned from last data, "false": no next page
       return queryParam ? queryParam : false
     },
   })
 
   return status === 'loading' ? (
     <p>Loading...</p>
   ) : status === 'error' ? (
     <p>Error: {error.message}</p>
   ) : (
     <>
       {data.pages.map((group, i) => (
         <React.Fragment key={i}>
           {group.projects.map(project => (
             <p key={project.id}>{project.name}</p>
           ))}
         </React.Fragment>
       ))}
       <div>
         <button
           onClick={() => fetchNextPage()}
           disabled={!hasNextPage || isFetchingNextPage}
         >
           {isFetchingNextPage
             ? 'Loading more...'
             : hasNextPage
             ? 'Load More'
             : 'Nothing more to load'}
         </button>
       </div>
       <div>{isFetching && !isFetchingNextPage ? 'Fetching...' : null}</div>
     </>
   )
 }

What the difference between react-query and apolloClient?

react-query doesn’t have normalized cache.

A “fully normalized cache” means that if multiple different queries return the same type of item with the same ID, it will only be stored once.

As an example, say you have a blog post app with /getPosts and /getPost/:postId endpoints. You make a request to /getPosts, and also to /getPost/3. Both endpoints return an item with {id: 3}. Do you end up with one object in the cache with that ID, or two objects in different places?

With a normalized cache, the client determines that “both of these items are of type Post, and both are ID 3″, so it only saves the one copy. With a non-normalized cache, the array of [{id: 1}, {id: 2}, {id: 3}] from /getPosts is saved separately from the {id: 3} returned by /getPost/3.

How to use json-server to simulate fake API call?

Install

sudo npm install -g json-server

Create fake response db.json

{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

Simulate fake API call

json-server --watch db.json --port 9000

Be the first to comment

Leave a Reply

Your email address will not be published.


*