Next.js Discord

Discord Forum

Data fetching

Answered
Blue Picardy Spaniel posted this in #help-forum
Open in Discord
Avatar
Blue Picardy SpanielOP
I have some data that I'm fetching on the server and showing to the client. I have a button which adds new data via server actions but what's the industry standard to update the data on the client?

66 Replies

Avatar
many people use a clientside fetching library for that like SWR or react query. I really recommend you to use one. With them you have more control over your data
Avatar
Blue Picardy SpanielOP
im not too familar with SWR or react query but would you recommend keeping client side state? so when i add new data via RSC i can just add to my already existing client side data
Avatar
Tibetan Spaniel
can't you save your fetched data from server into useState, then just update the state
Avatar
Blue Picardy SpanielOP
yeah thats what i was asking here
Avatar
Tibetan Spaniel
owh, sorry
Avatar
Blue Picardy SpanielOP
np, is that industry standard and recommended? im just trying to go about the best solution
Avatar
Tibetan Spaniel
I'll try to answer.
I don't know the industry standard, but actually it usually depends on your use case, like how big is the project.
I think u should consider if you really don't need to revalidate data from the server? And is it only one user at one time doing the update?

Adding to the existing client side data will result in faster ui, but i think , in general it's better and recommended to fetch new update from server (revalidating).
Avatar
yes, you can either combine them or keep them updating. Revalidating everytime the page again and again when new data comes in like aryomuzakki mentioned is not recommended, because your page would be slower like that
Avatar
Blue Picardy SpanielOP
so the best way is to update client state?
Avatar
is the data passed down from a ssr component?
Avatar
Blue Picardy SpanielOP
yeah
Avatar
so have the data in a useState
the initial value will be the data from the ssr component
Avatar
Blue Picardy SpanielOP
what if i need to pass data between client components?
Avatar
then watch with useEffect for the passed down data and set the new state if it updates
Avatar
Blue Picardy SpanielOP
wait wdym
Avatar
import { useEffect, useState } from "react";

export default function YourComponent({
    yourData
}: {
    yourData: any
}) {

    const [data, setData] = useState(yourData);

    useEffect(() => {
        setData(yourData);
    }, [yourData]);

    return (
        <>
            {data}
        </>
    )
}
yourData in this case always comes from the initial ssr value
u can pass that down to how many components u want
Avatar
Blue Picardy SpanielOP
the SSR data is fetched once when the page loads so it wont update
Avatar
what?
useState data will update when u trigger revalidatePath with server action
Avatar
Blue Picardy SpanielOP
wait whats revalidatePath
Avatar
Image
Avatar
Blue Picardy SpanielOP
let me show u my current setup
Avatar
oh i was thinking u know
Avatar
Blue Picardy SpanielOP
server component:
export default async function GlobalBanPortalPage() {
  const globalBans = await GlobalBanCollection.find({ }).toArray();

  return (
    <GlobalBans globalBans={globalBans} />
  );
}

client component:
export default function GlobalBans({
  globalBans
}: {
  globalBans: WithId<DbGlobalBan>[]
}) {
  const [data, setData] = useState<WithId<DbGlobalBan>[]>(globalBans);

  return (
    <NewGlobalBan setData={setData} />

    // display global bans with data
  );
}

client component:
export default function NewGlobalBan({
  setData
}: {
  setData: React.Dispatch<React.SetStateAction<WithId<DbGlobalBan>[]>>
}) {
  function banUser() {
    setData(prev => [...prev, {
      _id: offenderId,
      offense,
      evidence,
      notes,
      moderatorId: user.id,
      date: new Date()
    }]);
  }
}
Avatar
yeah in this case u are only updating the value via clientside
Avatar
Blue Picardy SpanielOP
nono i have server logic too
i just cut it out bc its too much
but is this good for the client
Avatar
yeah looks good
Avatar
Blue Picardy SpanielOP
so whats with the useeffect
Avatar
u basically listen for globalBans value to change
and update your state with the new value
Avatar
Blue Picardy SpanielOP
uhh all i have for the server is triggering a server action from the client that updates the database
Answer
Avatar
if u trigger this after doing your operations all ssr components in the current path will refetch the data
and useState is on clientside so u have to manually listen for changes and set the new value
Avatar
Blue Picardy SpanielOP
will that mean i dont have to pass setData to my NewGlobalBan component?
and instead i can just use useEffect?
Avatar
yeah
the logic in setData should be in server actions tho
but you know that probably
Avatar
Blue Picardy SpanielOP
wdym? setData is useState
Avatar
well no shit im just saying that banUser is only updating the state on clientside so i was saying that u can move this into your action and update your db or whatever
Avatar
Blue Picardy SpanielOP
holy shit... that's so much easier than what ive been doing wtf?
Avatar
yeah
and yk what
ive written a hook to just trigger revalidate when i want lmao
cuz im lazy af
Avatar
Blue Picardy SpanielOP
what would that look like?
Avatar
import {useFormState} from "react-dom";
import {handleRevalidate} from "@/actions/handleRevalidate";

export default function useRevalidator(path: string) {

    const [state, action] = useFormState(handleRevalidate, {
        revalidated: false,
        now: Date.now(),
        message: '',
    });

    const formData = new FormData();
    formData.append('p', path);

    async function revalidate(isInterval: boolean = false, interval: number = 1000) {
        if (isInterval) {
            const intervalId = setInterval(() => {
                action(formData);
            }, interval);
            return () => clearInterval(intervalId);
        } else {
            action(formData);
        }
    }

    return {
        state,
        revalidate,
    }
}
"use server";

import {revalidatePath} from "next/cache";

export const handleRevalidate = async (prevSate: any, formData: FormData) => {

    const path = formData.get('p')?.toString();

    if (!path) return {revalidated: false, now: Date.now(), message: 'Path not valid'};

    revalidatePath(path);

    return {revalidated: true, now: Date.now()};

}
there we go
magic
Avatar
Blue Picardy SpanielOP
o.o that looks complicated aha
Avatar
nah its easy so basically im imitating a server action request and just revalidating the passed in path
ive built in a interval feature so i can do some polling action without sockets
Avatar
Blue Picardy SpanielOP
well thanks for the help man, you've made my life a lot easier. i knew there was a better way
Avatar
no problem
u can mark my answer as solution if that helped u 👍
Avatar
Blue Picardy SpanielOP
i have looked at revalidate path before, it's come up a lot. i tried it once and it did nothing so i never thought anything of it haha, didn't know i had to use useEffect
👍