Next.js Discord

Discord Forum

How to fetch data from server in a moving client component

Answered
Yellowstripe scad posted this in #help-forum
Open in Discord
Yellowstripe scadOP
Server component:
import Client from "./client";

export default async function Home({ searchParams }: { searchParams: { query: string }}) {
    const query = searchParams.query
    let data;
    if (query) {
        const response = await fetch(`https://g8v07oi41l.execute-api.us-east-1.amazonaws.com/search?q=${query}`);
        data = await response.json()
    } else {
        data = { data: [] }
    
    }
    return (
        <div className="text-foreground min-h-screen">
            <Client data={data.data}/>
        </div>
    );
}


Client component:
export default function Client({ data }: { data: any[] }) {;
    let { replace } = useRouter()
    let pathname = usePathname()
    const [text, setText] = useState("")
    const [value] = useDebounce(text, 1000)
    const [isLoading, setLoading] = useState(false)
    const [isMoved, setMoved] = useState(true)
    const [imageLoading, setImageLoading] = useState(true)
    useEffect(() => {
        let params = new URLSearchParams(window.location.search)
        if (value) {
            params.set("query", value)
        } else {
            params.delete("query")
        }
        replace(`${pathname}?${params.toString()}`);
    }, [value])
    return (
        <div className="text-foreground min-h-screen">
            <div className={`${isMoved ? "mt-[17rem]" : ""} transition-all duration-300 mx-auto min-h-[calc(100vh-196px)] w-full max-w-[90rem] flex flex-col items-center px-6 pt-8 pb-16 md:px-20`}>
                <div className="relative max-w-2xl w-full">
                    <Input onChange={(e) => (setMoved(true), setLoading(true), setText(e.target.value))}
                        placeholder="Start typing"
                        className="h-16 text-lg shadow"
                        autoFocus
                    />
                    {isLoading && (
                        <div className="absolute right-0 top-0 bottom-0 flex items-center justify-center">
                            <Loader2 className="mr-6 text-black h-4 w-4 animate-spin" />
                        </div>
                    )}
                </div>
                <div className="w-full">
                    {!isMoved && <ol className="mt-10 grid grid-cols-1 gap-4 lg:grid-cols-3 lg:gap-x-8 xl:gap-x-4 xl:-gap-y-8">
                        {data.map((product) => (


My goal:
I want to setMoved(true), whenever the searchbar value is changed, then I want to debounce the searchbar value to 800ms. Once the data has been fetched, I want to setMoved(false), to display the results. If there are no results, I want to put <p>No results found</p>. If the search bar is empty I want to setMoved(true). My problem is whenever I search something, like "wordword", I get data 1, but when I remove "word", to make it just "word", data 1 flickers before data 2 is shown. Im not sure what the best way to do this is. I'd like to implement infinite scrolling later on.

Please let me know if this is confusing if you want to help, so I can explain more
Answered by Yellowstripe scad
I reverted it all back to the original client search, it was being search because my backend friend was searching through 200+ chars of description for around 4000 products in the database
View full answer

73 Replies

@Yellowstripe scad Server component: ts import Client from "./client"; export default async function Home({ searchParams }: { searchParams: { query: string }}) { const query = searchParams.query let data; if (query) { const response = await fetch(`https://g8v07oi41l.execute-api.us-east-1.amazonaws.com/search?q=${query}`); data = await response.json() } else { data = { data: [] } } return ( <div className="text-foreground min-h-screen"> <Client data={data.data}/> </div> ); } Client component: ts export default function Client({ data }: { data: any[] }) {; let { replace } = useRouter() let pathname = usePathname() const [text, setText] = useState("") const [value] = useDebounce(text, 1000) const [isLoading, setLoading] = useState(false) const [isMoved, setMoved] = useState(true) const [imageLoading, setImageLoading] = useState(true) useEffect(() => { let params = new URLSearchParams(window.location.search) if (value) { params.set("query", value) } else { params.delete("query") } replace(`${pathname}?${params.toString()}`); }, [value]) return ( <div className="text-foreground min-h-screen"> <div className={`${isMoved ? "mt-[17rem]" : ""} transition-all duration-300 mx-auto min-h-[calc(100vh-196px)] w-full max-w-[90rem] flex flex-col items-center px-6 pt-8 pb-16 md:px-20`}> <div className="relative max-w-2xl w-full"> <Input onChange={(e) => (setMoved(true), setLoading(true), setText(e.target.value))} placeholder="Start typing" className="h-16 text-lg shadow" autoFocus /> {isLoading && ( <div className="absolute right-0 top-0 bottom-0 flex items-center justify-center"> <Loader2 className="mr-6 text-black h-4 w-4 animate-spin" /> </div> )} </div> <div className="w-full"> {!isMoved && <ol className="mt-10 grid grid-cols-1 gap-4 lg:grid-cols-3 lg:gap-x-8 xl:gap-x-4 xl:-gap-y-8"> {data.map((product) => ( My goal: I want to setMoved(true), whenever the searchbar value is changed, then I want to debounce the searchbar value to 800ms. Once the data has been fetched, I want to setMoved(false), to display the results. If there are no results, I want to put <p>No results found</p>. If the search bar is empty I want to setMoved(true). My problem is whenever I search something, like "wordword", I get data 1, but when I remove "word", to make it just "word", data 1 flickers before data 2 is shown. Im not sure what the best way to do this is. I'd like to implement infinite scrolling later on. Please let me know if this is confusing if you want to help, so I can explain more
Can you simplify the code snippet to only show relevant code?
Yellowstripe scadOP
export default function Client({ data }: { data: any[] }) {;
    let { replace } = useRouter()
    let pathname = usePathname()
    const [text, setText] = useState("")
    const [value] = useDebounce(text, 1000)
    const [isLoading, setLoading] = useState(false)
    const [isMoved, setMoved] = useState(true)
    const [imageLoading, setImageLoading] = useState(true)
    useEffect(() => {
        let params = new URLSearchParams(window.location.search)
        if (value) {
            params.set("query", value)
        } else {
            params.delete("query")
        }
        replace(`${pathname}?${params.toString()}`);
        if (data.length === 0) {
            setMoved(false)
        }
        setLoading(false)
    }, [value])
    return (
            <Input onChange={(e) => (setMoved(true), setLoading(true), setText(e.target.value))}
              placeholder="Start typing"
              className="h-16 text-lg shadow"
              autoFocus
            />
            {isLoading && (
              <div className="absolute right-0 top-0 bottom-0 flex items-center justify-center">
                  <Loader2 className="mr-6 text-black h-4 w-4 animate-spin" />
              </div>
            )}
            {!isMoved && <ol className="mt-10 grid grid-cols-1 gap-4 lg:grid-cols-3 lg:gap-x-8 xl:gap-x-4 xl:-gap-y-8">
          {data.map((product) => (
            ))}
}
I think thats the shortest I can go
basically I want to convert this into a server side fetch
its slow
you can try searching for "multix"
I dont think its slow though?
its working well for me, such as switching between multix and via
i would suggest getting React Query though to cache the result in the client side
@aardani i would suggest getting React Query though to cache the result in the client side
Yellowstripe scadOP
how would I do this
Yellowstripe scadOP
lifesaver
Yeah or useSWR if you prefer lighter library
this way the previous result will pop up based on the client fetch
Server-side fetch is possible if you want slightly stale data. Such in the case that its okay that your product is using slightly outdated prefetched data
Yellowstripe scadOP
oh I see
@aardani https://www.youtube.com/watch?v=Tmn3vpymg-4
Yellowstripe scadOP
    const {data, isLoading, error} = useQuery({
        queryKey: ['search', debouncedSearchTerm],
        queryFn:
        () => {
            if (debouncedSearchTerm) {
                return fetch(`https://g8v07oi41l.execute-api.us-east-1.amazonaws.com/search?q=${search}`)
                .then(res => res.json(), setMoved(false))
            }
            return []
        }
    })

how would I setmoved(false) when it starts fetching and setMoved(true) when its done
@aardani or `isFetching` prop
Yellowstripe scadOP
    const {data, isLoading, error} = useQuery({
        queryKey: ['search', debouncedSearchTerm],
        queryFn:
        () => {
            if (debouncedSearchTerm) {
                return fetch(`https://g8v07oi41l.execute-api.us-east-1.amazonaws.com/search?q=${search}`)
                    .then(res => res.json())
            }
            return [];
        }
    })
    return (
        <div className="text-foreground min-h-screen">
            <div className={`${isLoading ? "mt-[17rem]" : ""} transition-all duration-300 mx-auto min-h-[calc(100vh-196px)] w-full max-w-[90rem] flex flex-col items-center px-6 pt-8 pb-16 md:px-20`}>
                <div className="w-full relative max-w-2xl flex">
                    <Input onChange={(e) => setSearch(e.target.value)}

how would I essentially set isLoading to true, whenever the value changes regardless of the debouncer
you can use a separate useState hook for that
but why though?
i thought the logic would be
1. user start typing
2. user input is debounced,
3. once debounce has stopped, pass value to react query
4. rq fetches and you show the "loading ui"
5. fetches done, display result and "Loading ui" is disabled
whats the use of debouncer if you want the result to instantly appear?
Yellowstripe scadOP
so before I had setMoved() and isMoved()
if its not moved I dont show any results, and if it is moved I show results
basically I want to show nothing while its loading other than the loading icon in the search bar
ah then you can combine the states
Yellowstripe scadOP
I basically want to replicate whats on stg.thock.gg right now
u can see how as soon as you start typing if you already have data it hides the results until the new ones are fetched
oh you want it to behave like that?
Yellowstripe scadOP
yea, my whole goal was to just make the search bar faster and more functional
The serach bar is alrady fast though
the only thing i assume it would make it faster is to show previously fetched result
Yellowstripe scadOP
which useQuery does
so now my goal is to have the same behavior, with useQuery
I just wish i could make the search results as instant as a serverside search
and use useState to move / hide things at the same time
What server side search are you looking for?
do you have any example?
Yellowstripe scadOP
yeah basically just a await fetch() in a server component
which is way quicker than fetch().then(...) in a client component
Those would still takes time...
@aardani Those would still takes time...
Yellowstripe scadOP
yeah I know but I've tested it before and its way quicker
Maybe before you dont use a debouncer?
Yellowstripe scadOP
I just can't move the elements like that
I did, in the search client component which set the params query
am I just entirely confused
Hmm
maybe create a demo
to compare the two
i dont see how it should differs unles you have a networking distance issue
Yellowstripe scadOP
yeah maybe it was that, but I dont have any problems anymore
Yellowstripe scadOP
@aardani I want to render isLoading as true when the page first loads so it starts as centered just like in the stg.thock.gg
but isLoading is not true when the page first loads, its only true when its fetching
@Yellowstripe scad Click to see attachment
You can just use search === "" then enable Loading no?
so isLoading || search === ""
Yellowstripe scadOP
ah thanks
@aardani so `isLoading || search === ""`
Yellowstripe scadOP
when I use this, when I first time something in it moves up even though there is no data
I feel so stupid
Sometimes it do be like that :))
Yellowstripe scadOP
any ideas lmao
@Yellowstripe scad any ideas lmao
You have to describe the intended behavior properly
@aardani You have to describe the intended behavior properly
Yellowstripe scadOP
I reverted it all back to the original client search, it was being search because my backend friend was searching through 200+ chars of description for around 4000 products in the database
Answer
Yellowstripe scadOP
now the search looks through things like title and variant
so its a lot faster
Lol
I knew its not a front end issue