Back to articles

Modern React Data Fetching: Beyond useEffect

AuthorMajd Muhtaseb05/11/20257 minutes
Modern React Data Fetching: Beyond useEffect

Modern React Data Fetching: Beyond useEffect

React's useEffect hook has long been the workhorse for data fetching. However, for complex applications, it can lead to verbose code and difficulties in managing caching, revalidation, and error handling. This article explores alternative approaches for more efficient and maintainable data fetching.

The Limitations of useEffect

While useEffect provides a basic mechanism for fetching data, it often requires manual implementation of common data fetching patterns:

  • Caching: Data is re-fetched on every component mount or dependency change unless manually cached.
  • Error Handling: Error states need to be managed manually.
  • Revalidation: Implementing background revalidation for stale data requires extra effort.
  • Race Conditions: Cleaning up asynchronous operations is crucial to avoid race conditions.

Introducing SWR

SWR (Stale-While-Revalidate) is a React Hooks library developed by Vercel for remote data fetching. It simplifies data fetching with built-in caching, revalidation, and error handling.

import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then(res => res.json())

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>

  return <div>hello {data.name}!</div>
}

In this example, useSWR handles the data fetching, caching, and revalidation of the /api/user endpoint. The fetcher function is a simple wrapper around fetch that parses the JSON response. SWR automatically revalidates the data in the background, ensuring that the UI stays up-to-date.

React Query

React Query is another popular library that provides robust data fetching, caching, synchronization and updating of server state. It offers features beyond SWR such as:

  • Mutations: Simplified way to handle POST, PUT, DELETE requests.
  • Pagination & Infinite Loading: Helpers for efficiently fetching and displaying large datasets.
  • Background Updates: Keeps data fresh even when the user is not actively using the application.
import { useQuery } from '@tanstack/react-query'

const fetchTodos = async () => {
  const response = await fetch('/api/todos')
  return response.json()
}

function Todos() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  })

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

Here, useQuery from React Query simplifies data fetching, caching, and error handling for the /api/todos endpoint. The query key is essential for React Query to uniquely identify and manage the query.

Conclusion

Moving beyond useEffect and adopting libraries like SWR or React Query can significantly improve the efficiency and maintainability of data fetching in React applications. These libraries provide built-in features for caching, revalidation, error handling, and more, allowing developers to focus on building features rather than managing data fetching intricacies. Choose the library that best suits your project's needs and complexity. Both offer excellent solutions for modern React development.