Yash Gorasia
Yash Gorasia

Reputation: 33

How and where should I fetch data for components that have their own API to fetch data from in a Dashboard of Next JS App

I am working on a Web App which is made using Next JS 15 ( App Router ).

It has a Dashboard in which there are many different widgets which shows data, which is fetched from API. All these widgets have it's own API to fetch data from. I can easily fetch data in their own server components and show Skeleton while the data is being fetched. But the problem here is I have many different filters too in each APIs. I have UI components in each widget to add filter to that particular widget so I need to fetch data from the API again when the user applies some filters. Now this is client interaction and Server Components doesn't support that. So I keep the UI component to apply filters as Client component but now how do I get the applied filters inside the server component and fetch new data based on that?

I tried...

  1. setting the applied filters as query parameters and getting them inside the Server Component as props (provided by Next JS). But when I set the filters in search params, there is a slight delay between the user interaction and actual setting of the search param. This feels less interactive and laggy.

Below is the server component in which I fetch data.

import fetchData from "@/lib/actions/fetch"

export default async function TotalEarningWidget({searchParams}) {

  const currentSearchParams = await searchParams


  const apiSearchParams = new URLSearchParams()
  apiSearchParams.append('filter', currentSearchParams.filter)
  
  
  // Fake API call using a helper function which handles the API call
  // I am not using any third party library for this, just a simple fetch API
  const totalEarningsData = await fetchData(`/total-earnings?${apiSearchParams.toString()}`)
  
    return <div>{/* ... */}</div>
}

And here is the client component from which I set filters to search params.

"use client"

import { useRouter, useSearchParams } from "next/navigation"

export default function FilterComponent() {
    const { replace } = useRouter()

    const currentSearchParams = useSearchParams()

    return (
        <div>
            <select
                defaultValue={currentSearchParams.get("filter")?.toString()}
                onChange={e => replace(`?filter=${e.target.value}`)}
            >
                <option value="1">Filter Option 1</option>
                <option value="2">Filter Option 2</option>
                <option value="3">Filter Option 3</option>
            </select>
        </div>
    )
}

  1. fetching the data client side with useState hook so I get the applied filter instantly and then set that applied filter to the URL so that I don't lose the applied filters if I refresh the page. I fetch the data Client Side using server actions so they don't expose my actual API URL. I don't know if this is a good approach but this works. But there is still an issue, when the dashboard is loading first time, it take a bit longer than it used to take when I was fetching data on server side. Is this because I switched to client side data fetching? Or is it something else that I might be missing?

Below is the client component in which I store applied filters and fetch data according to that.

"use client"

import { useSearchParams } from "next/navigation"
import { useEffect, useState } from "react"

export default function TotalEarningWidget({ searchParams }) {
    const [data, setData] = useState()

    const currentSearchParams = useSearchParams()

    const [appliedFilters, setAppliedFilters] = useState({
        filter: currentSearchParams.get("filter")?.toString(),
    })

    useEffect(() => {
        const fetchedData = fetchDataAction(appliedFilters.filter)

        setData(fetchedData)
    }, [appliedFilters])

    const handleFilterChange = filterToSet => {
        setAppliedFilters({ filter: filterToSet })
    }

    return <div>{/* ... */}</div>
}

Upvotes: 2

Views: 49

Answers (0)

Related Questions