React Query VS useEffect for Data Fetching in React Applications

·

3 min read

Choosing the right tool for data fetching is crucial for building efficient and maintainable React applications. Here's a breakdown of when to use useEffect vs. React Query:

useEffect:

  • Simple Data Fetching: When you only need to fetch data once on component mount or on specific events, useEffect is a good option. Its straightforward syntax makes it easy to understand and implement.

  • Basic State Management: For applications with minimal data fetching needs and manual handling of loading, error, and success states, useEffect can suffice.

Here's an example of using useEffect for simple data fetching and basic state management:

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  // Fetch data on component mount
  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      try {
        const response = await fetch('https://api.example.com/data');
        const fetchedData = await response.json();
        setData(fetchedData);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, []); // Empty dependency array ensures the effect runs only once on mount

  return (
    <div>
      {isLoading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}
      {data && (
        <ul>
          {data.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default MyComponent;

This example demonstrates:

  • Using useState hooks to manage data, loading, and error states.

  • useEffect with an empty dependency array to fetch data only once on component mount.

  • Conditional rendering based on the loading, error, and data states.

React Query:

  • Complex Data Fetching: React Query shines in scenarios with intricate data fetching requirements like:

    • Caching: Automatic caching of fetched data improves performance by reducing unnecessary API calls.

    • Automatic Refetching: Based on pre-defined conditions (e.g., stale data, focus events), React Query refetches data automatically.

    • Mutation Management: Simplifies handling data updates (POST, PUT, DELETE) with built-in optimistic updates and error handling.

    • Real-time Updates: Facilitates keeping your UI in sync with server-side changes through features like infinite queries and polling.

Here's an example showing React Query's functionalities for complex data fetching:

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

function MyComponent() {
  const { isLoading, error, data } = useQuery('todos', fetchTodos);

  const addTodo = async (text) => {
    const response = await fetch('https://api.example.com/todos', {
      method: 'POST',
      body: JSON.stringify({ text }),
    });
    return response.json();
  };

  const { mutate, isSuccess } = useMutation(addTodo, {
    onSuccess: (data) => {
      // Update cache after successful mutation
      queryClient.setQueryData('todos', (oldData) => [...oldData, data]);
    },
  });

  return (
    <div>
      {isLoading && <p>Loading todos...</p>}
      {error && <p>Error: {error.message}</p>}
      {data && (
        <ul>
          {data.map((todo) => (
            <li key={todo.id}>{todo.text}</li>
          ))}
        </ul>
      )}
      <input
        type="text"
        placeholder="Add todo"
        onChange={(e) => {
          mutate(e.target.value); // Trigger mutation on input change
        }}
      />
      {isSuccess && <p>Todo added successfully!</p>}
    </div>
  );
}

async function fetchTodos() {
  const response = await fetch('https://api.example.com/todos');
  return response.json();
}

export default MyComponent;

Choosing the Right Tool:

  • For simple applications with basic data fetching and state management, useEffect might be sufficient.

  • For complex applications demanding features like caching, refetching, mutations, and real-time updates, React Query is a powerful solution.

Additional Considerations:

  • Learning Curve: useEffect has a lower learning curve, while React Query has a more extensive API to grasp.

  • Code Maintainability: React Query can simplify complex data fetching logic, leading to cleaner and more maintainable code.

More info:

https://tanstack.com/query/latest/docs/framework/react/overview

https://spacema-dev.com/mastering-useeffect-in-react-a-comprehensive-guide/