ENDLESS Fetch loop with <Suspense> and Async component
Unanswered
Southern African anchovy posted this in #help-forum
Southern African anchovyOP
Hi, I am getting an issue where a component performing fetch while wrapped within suspense boundary causes sometimes an endless loop of fetch requests. I am doing the fetch without cache. Not posting code because it's literally just in testing where a component fetches and just writes out succes if fetch was succesful.
It doesnt happen if I do not use the suspense boundary, but I want the loading fallback
How else can I get the loading state functionality?
It doesnt happen if I do not use the suspense boundary, but I want the loading fallback
How else can I get the loading state functionality?
114 Replies
@Southern African anchovy Hi, I am getting an issue where a component performing fetch while wrapped within suspense boundary causes sometimes an endless loop of fetch requests. I am doing the fetch without cache. Not posting code because it's literally just in testing where a component fetches and just writes out succes if fetch was succesful.
It doesnt happen if I do not use the suspense boundary, but I want the loading fallback
How else can I get the loading state functionality?
could you show the code? because I don't get endless fetch loop
Southern African anchovyOP
async function ArticleBody({articleID} : {articleID:string}){
const body = makeArticle(makeData( await getArticleByID(articleID))?.[0].body as string);
//returns H1, Loading svg image, subtext, and body placeholder place
return(<>
{body}
</>)
}
export default ArticleBody;The getArticleID is just a wrapper for the fetch to handle any errors and to put togetherthe request itselg
MakeData and MakeArticle are just data handling functions to parse the results into the data I want
function Article({articleID, isCondensed} : {articleID : string, isCondensed : boolean}){
useEffect(() => {
console.log("debug")
},[]);
const [artHead, setArtHead] = useState(<ArticleHead articleID={articleID}/>);
const [body, setBody] = useState(!isCondensed ? <ArticleBody articleID={articleID}/> : null);
return (
<div className={`grid-container article-container ${articleID}-blocks`} id={`div-${articleID}`} key={`k-${articleID}`}>
<Suspense fallback={<ArticleLoading articleID={articleID} isCondensed={isCondensed} key={`k-loading-${articleID}`}/>}>
{artHead}
{body}
</Suspense>
</div>
)This is the code that actually instantiates the article body. Can it be because I am first setting the elements into state?
Nah still happens when I pull it out of state
@Southern African anchovy
function Article({articleID, isCondensed} : {articleID : string, isCondensed : boolean}){
useEffect(() => {
console.log("debug")
},[]);
const [artHead, setArtHead] = useState(<ArticleHead articleID={articleID}/>);
const [body, setBody] = useState(!isCondensed ? <ArticleBody articleID={articleID}/> : null);
return (
<div className={`grid-container article-container ${articleID}-blocks`} id={`div-${articleID}`} key={`k-${articleID}`}>
<Suspense fallback={<ArticleLoading articleID={articleID} isCondensed={isCondensed} key={`k-loading-${articleID}`}/>}>
{artHead}
{body}
</Suspense>
</div>
)
This is the code that actually instantiates the article body. Can it be because I am first setting the elements into state?
do you get endless fetch with this?
<Suspense fallback={<ArticleLoading articleID={articleID} isCondensed={isCondensed} key={`k-loading-${articleID}`}/>}>
<ArticleBody articleID={articleID}/>
</Suspense>Southern African anchovyOP
Yeah
I mean it's sometimes endless, sometimes just few hundred, sometimes correct amount
@Southern African anchovy Click to see attachment
could you create a new page and try this code?
import { Suspense } from "react";
export default function Page() {
return (
<>
<Suspense fallback={<div>loading...</div>}>
<Post />
</Suspense>
</>
);
}
async function Post() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const data = await res.json();
return (
<ul>
{data?.map((p: any) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}Southern African anchovyOP
Just for completeness sake
The make article just actually creates the react elements
export function makeData(response : Array<any>| undefined) : Array<IContents> | undefined{
if(!response || !response[0]){
return undefined;
}
let returnVal:Array<IContents> = response.map((x) : IContents => {
return {
identity : x?.attributes?.identity,
heading : x?.attributes?.heading,
subtext : x?.attributes?.subtext,
body : x?.attributes?.body,
tags : extractTags(x?.attributes?.tags)
} as IContents
});
return returnVal;
}
export async function getArticleByID(id:string) {
const fetchLink = `http://${host}/api/contents?populate[0]=tags&filters[identity][$eqi]=${id}`;
const res = await fetch(fetchLink, { cache: 'no-store' });
if (!res.ok) return undefined;
const resData = (await res.json())?.data;
return resData[0] == undefined ? undefined : resData;
}The make article just actually creates the react elements
oh then should be something wrong on other place
Southern African anchovyOP
Like what other place?
Because I am at a loss really
how does your root layout look like?
or could you share the repo?
Southern African anchovyOP
import '@/styles/layout.css'
import '@/styles/vanity.css'
import React from "react";
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body >
{children}
</body>
</html>
)
}what version of next are you using?
Southern African anchovyOP
From package.json
could you show
next.config.js?Southern African anchovyOP
Not much there
@Southern African anchovy Not much there
what is your node version?
Southern African anchovyOP
According to NVM
@Southern African anchovy According to NVM
could you try latest version of next?
Southern African anchovyOP
Which one is the latest?
Although not all the changes I did to try to fix it are commited there
@Southern African anchovy Which one is the latest?
npm install next@latest --savelet me test your repo
Southern African anchovyOP
14.1.0
@Southern African anchovy 14.1.0
what url should i test on your repo?
/content/[data]?Southern African anchovyOP
Well
That's bound to my private backend you cannot acces soo
But I suppose you can just change the specific fetch to a test one
Southern African anchovyOP
No issues there?
I don't see extra fetch here
@Southern African anchovy Click to see attachment
from your screenshot, those are client side fetching
Southern African anchovyOP
Yeah
WHich also should not happen
As it's not 'use client'
Unless I am missing something about how that directive works
Or how suspense works
could you push the code to your repo?
Southern African anchovyOP
Also are you on the dev branch?
Southern African anchovyOP
Cuz the main branch is not being used atm
ok so I should test on dev right?
Southern African anchovyOP
Ye
ok
Southern African anchovyOP
Does instantiating a server component from a client component implicitly make it client?
still no issue
Southern African anchovyOP
Just creating it within a client component
@Southern African anchovy Does instantiating a server component from a client component implicitly make it client?
oh yea your
ArticleManager is client componentSouthern African anchovyOP
Yeah
It kinda needs to know the client state to manage the article components
you could render server component with the children props of the client component
or any other props but not importing it
Southern African anchovyOP
The article components are meant to be just dumb articles with no actual JS attached to them
Wait uh... hold on, lemme make sure I undersand correctly
Southern African anchovyOP
How would that work?
Oh
oooh
ooooh
That might be the thing I missed
Can I then further manage those children from within the manager component?
like
<ArticleManager head={<ArticleHead />}>
<ArticleBody />
</ArticleManager>Southern African anchovyOP
The article mannager holds an array of articles, I am trying to build endless feed type of thing
@Southern African anchovy Can I then further manage those children from within the manager component?
yes like this
return (
<>
<div>{props.head}</div>
{children}
</>
)Southern African anchovyOP
The thing is even if I move everything client side explicitly the loop still happens
I am gonna try the children approach
The problen I now have is that I want to use search params (to know which article to display at), and to do that, I need to use client side hook...
Southern African anchovyOP
In the manager
Although I could put in couple placeholders
@Southern African anchovy In the manager
just pass it from the page
export default async function getArticle ({
//props,
params,
searchParams
}: {
params: { [key: string]: string},
searchParams:{[key:string]: string},
}){
return (
<ArticleManager searchParams={searchParams} />
)
}Southern African anchovyOP
The manager is the client side component here. Since I am creating an endless feed kind of thing it needs to be able to have access to the document and needs to be able to create the server side components.
(The article manager is the wrapper for all the articles)
if you pass the searchParams to it, it doesn't need to be client
Southern African anchovyOP
I think I am getting confused about the difference between client component and server component >>
Southern African anchovyOP
If the get article is client which it needs to be in order to get search params and it creates the article manager, it needs to import that to know what to create. WOuld that not put us where we started?
Southern African anchovyOP
Ye
you can leave it as server component
Southern African anchovyOP
Oh
I see
then pass the searchParams object to
ArticleManagerSouthern African anchovyOP
I mean, I think the article manager needs to be client side in order to have access to the displayed HTML document?
Or should I then have an extra client side component just for that?
Southern African anchovyOP
Well, the way I want it to work, is that I have categories within which are articles. I want those to be displayed in an endless feed like situation (think twitter or any other social media like that) and if you put in a URL for an article in the middle of the "stack" I want to load both articles above and bellow. If I use loading proxy the scrolling position can get all wonky so I made a hook to correct for that.
like infinity scroll?
Southern African anchovyOP
Yeah
you could fetch the first page data on server and pass it to a react context
and fetch the next page on client
Southern African anchovyOP
The article manager is also supposed to be responsible for initiating the fetches of following articles
yeah so leave it as a server component
Southern African anchovyOP
And then add another client side component for the dynamic stuff
I suppose
But
Am trying to wrap my head around this
Southern African anchovyOP
Could I just simplyfy it and pass in a generator function that returns the server side article? Like as a propp pass in a function. That way it doesnt have to import any server side components
Southern African anchovyOP
Yeah
@Southern African anchovy Yeah
yeah, you could use server action with it
@Ray and fetch the next page on client
like what i said, fetch the next page on client when it is about the reach the end
Southern African anchovyOP
Yeah I suppose, I was just not sure what exactly it meant
Next has too many things with too many similar names