Today we will learn about a small react package to handle data fetching to make our life easier.
React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes fetching, caching, synchronizing and updating server state in your React applications a breeze.
-
Help you remove many lines of complicated and misunderstood code from your application and replace with just a handful of lines of React Query logic.
-
Make your application more maintainable and easier to build new features without worrying about wiring up new server state data sources
-
Have a direct impact on your end-users by making your application feel faster and more responsive than ever before.
-
Potentially help you save on bandwidth and increase memory performance
Creating demo and fetching data
Let's create a simple app that fetches random Chuck Norris jokes using react-query
For that, we'll create a demo app
npx create-react-app demo
cd demo
yarn install
yarn add react-query
Now, let's remove all the boilerplate code inside, and add some code of our own.
We'll import the useQuery
function from react-query
and use it to fetch some jokes.
import {useQuery} from 'react-query'; const query = useQuery('chuck_norris', async () => { return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) })
The full code will be
import './App.css';
import {useQuery} from 'react-query'; function App() { const query = useQuery('chuck_norris', async () => { return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) }) return ( <div className="App"> {query.data.value} </div> );
} export default App;
Now, when we run this, react will throw us an error
This is because, our api still hasn't fetched the data yet. So, we need to modify our code to show Loading...
when the data is being fetched.
return ( <div className="App"> {query.data?.value || 'Loading...' } </div> );
Now if we run our app
We can use the built in statuses that comes with react-query
to make our life much easier
import './App.css';
import {useQuery} from 'react-query'; function App() { const query = useQuery('chuck_norris', async () => { await new Promise(resolve => setTimeout(resolve, 1000)) // add a delay return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) }) return ( <div className="App"> {query.isLoading ? 'Loading...' : query.data.value} // using the query.isLoading status </div> );
} export default App;
Introducing API errors
Now., let's say we want to handle a case where our API has wen't down. So, let's throw an exception on our code
import './App.css';
import {useQuery} from 'react-query'; function App() { const query = useQuery('chuck_norris', async () => { await new Promise(resolve => setTimeout(resolve, 1000)) throw new Error('API Down') // Force the promise to fail return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) }) return ( <div className="App"> {query.isLoading ? 'Loading...' : query.data.value} </div> );
} export default App;
Let's see what happens if we run our code now Seems we're left hanging on the error messege, and after a delay, our app will breakdown. That's obviously not a good sign for our users. So, instead let's modify our code to handle this case
import './App.css';
import {useQuery} from 'react-query'; function App() { const query = useQuery('chuck_norris', async () => { await new Promise(resolve => setTimeout(resolve, 2000)) throw new Error('API Down') return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) }) return ( <div className="App"> { query.isLoading ? 'Loading...' : query.isError ? // another useful builtin status check query.error.message : query.data.value } </div> );
} export default App;
If we run this
React Query Devtools
react-query
comes with an awesome tool that lets us check the state of our query while developing. To install that, run
yarn add react-query-devtools
and import it in our code, we also need to add the ReactQueryDevtools
component at the bottom of our div
to render it.
import './App.css'
import {useQuery} from 'react-query'
import {ReactQueryDevtools} from 'react-query-devtools' function App() { const query = useQuery('chuck_norris', async () => { await new Promise(resolve => setTimeout(resolve, 2000)) return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) }) return ( <div className="App"> { query.isLoading ? 'Loading...' : query.isError ? query.error.message : query.data.value } <ReactQueryDevtools /> </div> );
} export default App;
If we run it, we'll see a small button at the bottom of our code, clicking this will open up the devtool From here, we can check the state of our cache, the configurations, how many times was it fetched, and many more info.
Adding Stale Time
One thing you'll find weird, is the react-query
will try to fetch new data every time the browser window came out of focus.
This is because by default react-query
is configured to set query result to statle as soon as the data is fetched. Ofcourse this is not useful for our case, and we can modify the stale timer on our api
import './App.css'
import {useQuery} from 'react-query'
import {ReactQueryDevtools} from 'react-query-devtools' function App() { const query = useQuery('chuck_norris', async () => { await new Promise(resolve => setTimeout(resolve, 1000)) return fetch('https://api.chucknorris.io/jokes/random') .then(res => res.json()) }, { staleTime: 5000 // Add the amount here, 0 for always, Infinity for never } ) return ( <div className="App"> { query.isLoading ? 'Loading...' : query.isError ? query.error.message : query.data.value } <ReactQueryDevtools /> </div> );
} export default App;
Now if we run it