React Query vs Traditional CRUD with Fetch/Axios

React Query vs Traditional CRUD with Fetch/Axios

Managing data in React apps often starts with simple CRUD operations using fetch or Axios. As your app grows, handling loading states, caching, refetching, and error management becomes complex. React Query is a powerful library that simplifies data fetching and state management. In this post, we’ll compare React Query with traditional approaches and show why React Query is a game-changer for modern React development.

Traditional CRUD Operations with Fetch/Axios

Most React apps begin by fetching data with fetch or Axios inside useEffect. Here’s a typical example:


import { useState, useEffect } from 'react';

function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}
  

With Axios, the code is similar:


import axios from 'axios';

useEffect(() => {
  axios.get('/api/users')
    .then(res => setUsers(res.data))
    .catch(err => setError(err));
}, []);
  

Challenges with Fetch/Axios

  • Manual loading and error state management
  • No built-in caching or refetching
  • Hard to synchronize data across components
  • Complexity increases with pagination, mutations, and optimistic updates
  • No automatic background updates

React Query: The Modern Solution

React Query abstracts away the boilerplate of data fetching, caching, and state management. It provides hooks for queries and mutations, handles loading and error states, and keeps your UI in sync with your backend.

  • Automatic Caching: Data is cached and reused across components.
  • Background Refetching: Keeps data fresh automatically.
  • Built-In Loading/Error States: No need to manage these manually.
  • Mutations: Simplifies POST, PUT, DELETE operations.
  • Optimistic Updates: Instantly update UI before server response.
  • Pagination & Infinite Queries: Easy to implement with built-in hooks.
  • Devtools: Visualize query state and cache in development.

Example: Fetching Data with React Query


import { useQuery } from '@tanstack/react-query';

function Users() {
  const { data, isLoading, error } = useQuery(['users'], () =>
    fetch('/api/users').then(res => res.json())
  );

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {data.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}
  

Mutations with React Query


import { useMutation, useQueryClient } from '@tanstack/react-query';

const queryClient = useQueryClient();

const mutation = useMutation(newUser =>
  fetch('/api/users', {
    method: 'POST',
    body: JSON.stringify(newUser),
    headers: { 'Content-Type': 'application/json' }
  }).then(res => res.json()),
  {
    onSuccess: () => {
      queryClient.invalidateQueries(['users']);
    }
  }
);
  

Why Choose React Query?

  • Less boilerplate, more productivity
  • Automatic cache and refetching
  • Easy to scale for large apps
  • Better user experience with instant updates
  • Works with fetch, Axios, or any data source

Conclusion

While fetch and Axios are great for simple apps, React Query is the best choice for scalable, maintainable, and performant React applications. It handles the complexity of data fetching so you can focus on building great user experiences.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *