5 Ways to Fetch Data in React.js with Next.js Examples

Ways to Fetch Data in React.js with Next.js Examples

Data fetching is a fundamental requirement for building modern web applications, especially in React-based applications. In this article, we will explore different ways to fetch data in React.js with Next.js examples. We will look at popular libraries such as Axios and React Query and compare them with built-in browser APIs such as Fetch and Async-Await. Additionally, we will discuss the use of custom hooks for data fetching and management.

Fetch Method

The Fetch method is a built-in browser API that simplifies making HTTP requests to a server to fetch data in JSON or XML format. It returns a promise, which can be used to handle the response data or catch errors.

components/FetchExample.tsx
import { useState, useEffect } from 'react'

interface Post {
  id: number;
  title: string;
  body: string;
}

export default function FetchExample(): JSX.Element {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(data => {
        setPosts(data);
      })
      .catch(error => {
        console.error(error);
      });
  }, []);

  return (
    <div>
    	<h2>Async-Await Example</h2>
      	{posts.map(post => (
	        <div key={post.id}>
	          <h3>{post.title}</h3>
	          <p>{post.body}</p>
	        </div>
      	))}
    </div>
  );
}

Fetch Method Pros and Cons

Pros:

  • Simple and easy to understand.
  • Built-in browser API.
  • Supports basic error handling.

Cons:

  • Requires more verbose error handling.
  • Doesn't support cancellation by default.
  • Can't handle more complex use cases easily.

Async-Await

Async-Await is a syntax for handling asynchronous operations in JavaScript. It allows us to write asynchronous code in a more synchronous style, making it easier to read and write. This approach is preferred for fetching data from APIs because it simplifies error handling and removes the need for .then callbacks. The await keyword can be used to pause the code and wait for a promise to resolve.

components/AsyncAwaitExample.tsx
import React, { useState, useEffect } from 'react';

interface Post {
  id: number;
  title: string;
  body: string;
}

export default function AsyncAwaitExample(): JSX.Element {
  const [posts, setPosts] = useState<Post[]>([]);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts');
        const data = await response.json();
        setPosts(data);
      } catch (error) {
        setError(error);
      }
    };

    fetchPosts();
  }, []);

  if (error) {
    return (
      <div>
        <p>An error occurred: {error.message}</p>
      </div>
    );
  }

  return (
    <div>
      <h2>Async-Await Example</h2>
      {posts.map(post => (
        <div key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

Async-Await Pros and Cons

Pros:

  • Easier to read and write than the fetch method.
  • Supports more complex use cases and error handling.
  • Built-in support for cancellation.

Cons:

  • Still requires manual error handling.
  • Can become more complex for more complex use cases.
  • No built-in caching mechanism.

Axios library

Axios is a popular third-party library for making HTTP requests in JavaScript. It simplifies sending asynchronous HTTP requests to REST APIs, and supports common CRUD (create, read, update, delete) operations. Axios can be used in plain JavaScript or with any library accordingly, and provides features such as request and response interception, progress tracking, and more.

components/AxiosExample.tsx
import { useState, useEffect } from 'react'
import axios from 'axios'

interface Post {
  id: number;
  title: string;
  body: string;
}

export default function AxiosExample(): JSX.Element {
  const [posts, setPosts] = useState<Post[]>([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get<Post[]>('https://jsonplaceholder.typicode.com/posts');
        setPosts(response.data);
      } catch (error) {
        console.error(error);
      }
    };

    fetchData();
  }, []);

  return (
    <div>
      <h2>Axios Example</h2>
      {posts.map((post) => (
        <div key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
};

Axios library Pros and Cons

Pros:

  • Simplifies HTTP requests and response handling.
  • Supports error handling and cancellation.
  • Built-in support for progress tracking.

Cons:

  • Adds an extra dependency.
  • Overkill for simple requests.
  • Learning curve of third party technology.

Custom Hook

A Custom Hook is a function that starts with the prefix use and can use one or more React hooks inside them. It allows for the reuse of logic and stateful logic between different components. Custom Hooks can be used to encapsulate common logic, such as data fetching, and abstract it away from the components. By doing this, it provides a cleaner separation of concerns, making it easier to reason about the code and reuse it in multiple places.

components/UseFetchExample.tsx
import { useState, useEffect } from 'react';

interface Post {
  id: number;
  title: string;
  body: string;
}

export function UseFetchExample(url: string): Post[] {
  const [data, setData] = useState<Post[]>([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
      } catch (error) {
        console.error(error);
      }
    };

    fetchData();
  }, [url]);

  return data;
}

Custom Hook Usage in the Component

To use a Custom Hook, import it into your component and call it like any other React Hook, passing in any necessary arguments. In the case of a data fetching hook, you would typically pass in the URL of the API endpoint from which you want to fetch data. The hook will return the fetched data, which you can then use in your component. This allows for separation of concerns and reusability of logic across different components, making it easier to maintain and update the code.

pages/index.tsx
import {UseFetchExample} from '../components/UseFetchExample'

export default function Home(): JSX.Element {
  const posts = UseFetchExample('https://jsonplaceholder.typicode.com/posts')

  return (
		<main>        
			<h2>Custom Hook Example</h2>
			{posts.map((post) => (
			  <div key={post.id}>
			    <h3>{post.title}</h3>
			    <p>{post.body}</p>
			  </div>
			))}
		</main>
  )
}

Custom Hook Pros and Cons

Pros:

  • Encapsulates data fetching logic in a reusable function.
  • Can support more complex use cases with ease.
  • Allows for simpler and more concise code.

Cons:

  • Requires more setup and understanding.
  • May be overkill for simpler use cases.
  • No built-in caching mechanism.

React Query library

React Query is a powerful library for data fetching and management in React applications. It provides features such as caching and automatic refetching, which can greatly improve the user experience by reducing load times and preventing data irregularities. Additionally, React Query provides advanced features like pagination, polling, and query caching, making it a robust solution for managing data in complex applications.

components/ReactQueryExample.tsx
import { useQuery } from 'react-query';

interface Post {
  id: number;
  title: string;
  body: string;
}

export default function ReactQueryExample(): JSX.Element {

  const { isLoading, error, data } = useQuery<Post[]>('posts', async () => {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts');
    const json = await response.json();
    return json;
  });

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>An error has occurred: {error.message}</div>;
  }

  return (
    <div>
      <h2>ReactQuery Example</h2>
      {data.map((post) => (
        <div key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

React Query Usage in a Component

pages/index.tsx
import { QueryClient, QueryClientProvider} from 'react-query';

const queryClient = new QueryClient();

export default function Home() {
  
  return (
    <main>
      <QueryClientProvider client={queryClient}>
        <ReactQueryExample/>
      </QueryClientProvider>
    </main>
  )
}

React Query Library Pros and Cons

Pros:

  • Simplifies data fetching and management in React applications.
  • Provides advanced features like caching, refetching, pagination, and polling.
  • Improves overall user experience by reducing load times and preventing data irregularities.
  • Automatic error handling and caching.
  • Can help reduce code complexity and improve maintainability.

Cons:

  • Adds an extra dependency to your project.
  • Can be more complex to set up and understand compared to simpler solutions.
  • May not be necessary for simpler applications that only require basic data fetching functionality.

Conclusion

Choosing the right data fetching approach in React can have a significant impact on the overall user experience and application performance. In this article, we explored various options for fetching data in React applications, including built-in browser APIs like Fetch and Async-Await, third-party libraries like Axios and React Query, and custom hooks. Each option has its own pros and cons and should be chosen based on the specific use case and requirements of the application. Here is the Github repository link for examples of how to implement these approaches: https://github.com/space-rocket/ways-to-fetch-data-with-react. That's it for today. Thanks for reading!