Search code examples
reactjsnext.jsnext.js13nextjs14

Best Way to Fetch Data in a Client Component in Next.js 14: useEffect with Route Handlers vs Async Server Action


I'm working on a Next.js 14 project and I need to fetch data in a client component. I've come across two different methods and I'm unsure which one is the best practice.

Method 1: useEffect with an Async Function and Server Action

In this method, I use useEffect with an async function to fetch data directly from a server action that I also use in my server components. This way, I can reuse my server actions for both fetching and mutating data.

import React, { useEffect, useState } from 'react';
import { fetchIngresosForMonth } from './ServerActions'; // Server action

const ClientComponent = () => {
  const [data, setData] = useState<ExtendedIngresosRow[]>([]);

  useEffect(() => {
    async function fetchData() {
      try {
        const fetchedData = await fetchIngresosForMonth();
        console.log("Fetched data SERVER ACTION INGRESOS:", fetchedData);
        setData(fetchedData);
      } catch (error) {
        console.error("Error fetching data SERVER ACTION INGRESOS:", error);
      }
    }
    fetchData();
  }, []);

  return (
    <div>
      {/* Render data */}
    </div>
  );
};

export default ClientComponent;

Method 2: useEffect with Route Handlers

In this method, I set up an API route using a Route handler and fetch the data within useEffect by making a call to this route. According to the Next.js documentation, this seems to be the recommended way for fetching data in client components.

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

const ClientComponent = () => {
  const [data, setData] = useState<ExtendedIngresosRow[]>([]);

  useEffect(() => {
    fetch('/api/IngresosRoutes/getIngresosDelMes')
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then(result => {
        console.log("Fetched data:", result.resultOk);
        setData(result.resultOk);
      })
      .catch(error => {
        console.error("Error fetching data:", error);
      });
  }, []);

  return (
    <div>
      {/* Render data */}
    </div>
  );
};

export default ClientComponent;

My Question

I prefer using the first method because it allows me to reuse server actions for both fetching and mutating data. However, the Next.js documentation states that server actions are primarily for mutating data and not for fetching data in client components. It also recommends using Route handlers for fetching data.

  • What is the best practice for fetching data in a client component in Next.js 14?
  • Is it acceptable to use server actions within useEffect for data fetching in a client component, or should I strictly use Route handlers for this purpose?
  • Are there any performance or architectural considerations I should be aware of when choosing between these methods?

Any insights or recommendations would be greatly appreciated. Thank you!


Solution

  • By: joulev

    What is the best practice for fetching data in a client component in Next.js 14?

    route handlers with data fetching libraries, such as react-query or swr.

    Is it acceptable to use server actions within useEffect for data fetching in a client component, or should I strictly use Route handlers for this purpose?

    no, see below. it's best to use data fetching libraries, but if you really want you can still use useEffect – but route handlers are required. why? see below.

    Are there any performance or architectural considerations I should be aware of when choosing between these methods?

    server actions cannot be run in parallel, so that completely disqualifies them from being a good data querying method.