React Query is a powerful library for managing server-state in React applications. It simplifies fetching, caching, synchronizing, and updating server state. Here’s a guide to effectively handling asynchronous operations using React Query.
1. Setting Up the Project
Initialize the Project:
npx create-react-app react-query-demo --template typescriptcd react-query-demo
Install React Query:
npm install react-querynpm install @types/react-query
2. Setting Up React Query Provider
- Wrap Your App with QueryClientProvider:// src/index.tsx
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import { QueryClient, QueryClientProvider } from 'react-query'; const queryClient = new QueryClient(); ReactDOM.render( <React.StrictMode> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </React.StrictMode>, document.getElementById('root') );
3. Fetching Data
Create a Fetch Function:
// src/api.tsexport const fetchUsers = async () => { const response = await fetch('https://jsonplaceholder.typicode.com/users'); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); };
Use useQuery Hook:
// src/components/UserList.tsximport React from 'react'; import { useQuery } from 'react-query'; import { fetchUsers } from '../api'; const UserList: React.FC = () => { const { data, error, isLoading } = useQuery('users', fetchUsers); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {(error as Error).message}</div>; return ( <div> <h1>User List</h1> <ul> {data.map((user: { id: number; name: string }) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default UserList;
4. Mutations
Create a Mutation Function:
// src/api.tsexport const addUser = async (user: { name: string }) => { const response = await fetch('https://jsonplaceholder.typicode.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(user), }); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); };
Use useMutation Hook:
// src/components/AddUser.tsximport React, { useState } from 'react'; import { useMutation, useQueryClient } from 'react-query'; import { addUser } from '../api'; const AddUser: React.FC = () => { const [name, setName] = useState(''); const queryClient = useQueryClient(); const mutation = useMutation(addUser, { onSuccess: () => { queryClient.invalidateQueries('users'); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); mutation.mutate({ name }); setName(''); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter name" required /> <button type="submit">Add User</button> {mutation.isError && <div>Error adding user</div>} {mutation.isSuccess && <div>User added successfully</div>} </form> ); }; export default AddUser;
5. Error Handling
- Handle Errors Gracefully:// src/components/UserList.tsx
import React from 'react'; import { useQuery } from 'react-query'; import { fetchUsers } from '../api'; const UserList: React.FC = () => { const { data, error, isLoading } = useQuery('users', fetchUsers); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {(error as Error).message}</div>; return ( <div> <h1>User List</h1> <ul> {data.map((user: { id: number; name: string }) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default UserList;
6. Optimistic Updates
- Implement Optimistic Updates:// src/components/AddUser.tsx
import React, { useState } from 'react'; import { useMutation, useQueryClient } from 'react-query'; import { addUser } from '../api'; const AddUser: React.FC = () => { const [name, setName] = useState(''); const queryClient = useQueryClient(); const mutation = useMutation(addUser, { onMutate: async (newUser) => { await queryClient.cancelQueries('users'); const previousUsers = queryClient.getQueryData('users'); queryClient.setQueryData('users', (old: any) => [...old, { id: Date.now(), ...newUser }]); return { previousUsers }; }, onError: (err, newUser, context) => { queryClient.setQueryData('users', context?.previousUsers); }, onSettled: () => { queryClient.invalidateQueries('users'); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); mutation.mutate({ name }); setName(''); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter name" required /> <button type="submit">Add User</button> {mutation.isError && <div>Error adding user</div>} {mutation.isSuccess && <div>User added successfully</div>} </form> ); }; export default AddUser;
7. Caching and Invalidating Data
- Invalidate Queries:// src/components/UserList.tsx
import React from 'react'; import { useQuery, useQueryClient } from 'react-query'; import { fetchUsers } from '../api'; const UserList: React.FC = () => { const queryClient = useQueryClient(); const { data, error, isLoading } = useQuery('users', fetchUsers); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {(error as Error).message}</div>; return ( <div> <h1>User List</h1> <button onClick={() => queryClient.invalidateQueries('users')}>Refresh</button> <ul> {data.map((user: { id: number; name: string }) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default UserList;
By following these steps, you can effectively manage asynchronous operations in your React applications using React Query. This approach simplifies data fetching, caching, synchronization, and updating, leading to a more robust and scalable application.
Social Plugin