Data fetching is an essential part of building modern web applications. As there is no specific approach to fetch data from API in React, developers do it in their own ways. One of the most popular method is to use React's hook useEffect
.
Disadvantages of existing data fetching techniques (useEffect)
One of the biggest disadvantages of using the useEffect hook for data fetching is the unnecessary re-renders that can occur. This can lead to poor performance, especially in complex applications with multiple components and large data sets. Additionally, managing state and handling errors can become challenging when using useEffect.
Intro to React Query
React Query is a library that makes it easy to fetch, cache, and manage data in your React applications. It provides a simple and effective solution for data fetching that overcomes many of the disadvantages of using useEffect.
Implementation
Enough talk, let's code now.
Creating React App
Let's create a react app first.
npx create-react-app react-query-guide
cd react-query-guide
npm start
or
yarn create react-app react-query-guide
cd react-query-guide
yarn start
Your app would be live on http://localhost:3000
Install react-query
npm install react-query
or
yarn add react-query
Setup JSON Server
Now let's setup a json server for users API
Install json-server
package:
npm install json-server
or
yarn add json-server
Create a file db.json
and populate following data in it:
{
"users": [
{
"id": 1,
"name": "Bilal Riaz"
},
{
"id": 2,
"name": "Haris Ejaz"
},
{
"id": 3,
"name": "Hamza Hameed"
},
{
"id": 4,
"name": "Zeeshan Ali"
}
]
}
Add a script in package.json
file as:
"serve-json": "json-server --watch db.json --port 4000"
Then execute
npm run serve-json
or
yarn run serve-json
Your server would be running on http://localhost:4000
Introducing React Query
Now it's time to use React Query in our app. Let's update our App.js
file:
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
const App = () => {
return (
<QueryClientProvider client={queryClient}>
<div>App</div>
</QueryClientProvider>
);
};
export default App;
We have created a queryClient
with the use of QueryClient
, provide it to QueryClientProvider
and wrap the whole app with it. This is essential to make use of React Query's features throughout the app.
Fetching Data with useQuery
Now it's time to make network requests. Let's install axios
for this purpose.
Install axios
:
npm install axios
or
yarn add axios
Create a component Users.js
to display fetched users from API.
import axios from 'axios';
import { useQuery } from 'react-query';
const fetchUsers = () => axios.get('http://localhost:4000/users');
const Users = () => {
const { isLoading, data } = useQuery({
queryKey: 'users',
queryFn: fetchUsers,
});
if (isLoading) {
return <h2>Loading...</h2>;
}
return (
<div>
{data?.data?.map((user) => (
<p key={user?.id}>{user?.name}</p>
))}
</div>
);
};
export default Users;
Here we're using useQuery
hook to fetch users data. We have provided a unique queryKey
and a function fetchUsers
which returns a promise to queryFn
property.
We get several properties from useQuery
hook as two of them isLoading
& data
have been shown in the code.
Then make use of Users.js
component in App.js
:
import { QueryClient, QueryClientProvider } from 'react-query';
import Users from './Users';
const queryClient = new QueryClient();
const App = () => {
return (
<QueryClientProvider client={queryClient}>
<Users />
</QueryClientProvider>
);
};
export default App;
We have successfully fetched users with the use of React Query.
cacheTime
By default, your query remains in the cache for 5 minutes but you can configure it as:
const { isLoading, data } = useQuery({
queryKey: 'users',
queryFn: fetchUsers,
cacheTime: 2000
});
You can configure cacheTime
in milliseconds.
You are probably thinking what actually cacheTime means? Basically it is the time for how long your query would be cached. For example, you fetch users with queryKey: "users"
, fetching users would bring data from cache
and fetching from API continues in the background and then updates the cached data with the data from API.
staleTime
By default, every query will trigger a network request if you are fetching something from API. But you can use the cached data instead of making network request again & again. It is helpful in the case where we know that the data coming from API is static as it doesnβt often change. To do so, you can configure staleTime
in milliseconds as:
const { isLoading, data } = useQuery({
queryKey: 'users',
queryFn: fetchUsers,
staleTime: 30000 //0ms by default
}
Fetching Data on Button Click
Till now, API request is being made when component mounts. If we want to fetch users on some button click, we would configure useQuery
hook as:
const { isLoading, data, refetch } = useQuery({
queryKey: 'users',
queryFn: fetchUsers,
enabled: false
}
refetch
is a function to run the queryFn
function.
We would add a button in jsx and configure it to fetch users on clicking it.
<div>
<button onClick={refetch}>Fetch Users</button>
{data?.data?.map((user) => (
<p key={user?.id}>{user?.name}</p>
))}
</div>
Posting Data with useMutation
Enough for fetching data, it's time to talk about posting/updating data. We would use useMutation
hook for this purpose. Let's implement adding user feature with it.
Update your Users.js
file as:
import axios from 'axios';
import { useMutation, useQuery } from 'react-query';
const fetchUsers = () => axios.get('http://localhost:4000/users');
const addNewUser = () =>
axios.post('http://localhost:4000/users', {
name: 'Ali Zuberi',
});
const Users = () => {
const { isLoading, data, refetch } = useQuery({
queryKey: 'users',
queryFn: fetchUsers,
});
const { mutate } = useMutation(addNewUser);
if (isLoading) {
return <h2>Loading...</h2>;
}
return (
<div>
<button onClick={refetch}>Fetch Users</button>
<button onClick={mutate}>Add User</button>
{data?.data?.map((user) => (
<p key={user?.id}>{user?.name}</p>
))}
</div>
);
};
export default Users;
We have created addNewUser
function which return a promise and assign it to the useMutation
hook. We have extracted out mutate
function from the hook which would be called on clicking Add User
button.
Refetch Data After Mutation
New user can be added successfully till now. But the issue is that adding a new user is not re-fetching the updated users list. To do so, configure useMutation
with onSuccess
function as:
const { mutate } = useMutation(addNewUser, {
onSuccess: refetch,
});
Mutate Data with Dynamic Values
If you want to add a new user with the name provided by an input, update Users.js
file as:
import axios from 'axios';
import { useState } from 'react';
import { useMutation, useQuery } from 'react-query';
const fetchUsers = () => axios.get('http://localhost:4000/users');
const addNewUser = (userName) =>
axios.post('http://localhost:4000/users', {
name: userName,
});
const Users = () => {
const [username, setUsername] = useState('');
const { isLoading, data, refetch } = useQuery({
queryKey: 'users',
queryFn: fetchUsers,
});
const { mutate } = useMutation(addNewUser, {
onSuccess: refetch,
});
if (isLoading) {
return <h2>Loading...</h2>;
}
return (
<div>
<div>
<input
placeholder='Enter name'
value={username}
onChange={(e) => setUsername(e?.target?.value)}
/>
</div>
<button onClick={() => mutate(username)}>Add User</button>
<button onClick={refetch}>Fetch Users</button>
{data?.data?.map((user) => (
<p key={user?.id}>{user?.name}</p>
))}
</div>
);
};
export default Users;
Conclusion
React Query is a powerful tool for simplifying data management in React applications, making it easier to fetch, cache, and manage data with ease. Whether you're a beginner or an experienced developer, incorporating React Query into your workflow can greatly enhance the performance and efficiency of your applications. So, start exploring its capabilities today and elevate your React development game.
Links
Here is the Github repository for the code covered in this blog.
Co-authored by - harisbinejaz
Top comments (0)