Suspense fallback is not working in nextjs 14
Unanswered
Segugio Italiano posted this in #help-forum
Segugio ItalianoOP
When I am routing to this route using next link, my page is blocked from rendering for 2 seconds. Instead I want to show the headings and their own fallbacks as a loading state untill the promise is resolved or I get data from the database.
loading.tsx in the same level is working but that will allow to render the whole page to show the loader which I do not want. The headings or other static text should be there and the data which is fetched from database should have that loading state untill data is fetched.
loading.tsx in the same level is working but that will allow to render the whole page to show the loader which I do not want. The headings or other static text should be there and the data which is fetched from database should have that loading state untill data is fetched.
import React, { Suspense } from "react"
export const dynamic = "force-dynamic"
const wait = async () => {
let data = {
data: "some data",
}
await new Promise((resolve) => setTimeout(resolve, 2000))
return data
}
const TestRoute = async () => {
let { data } = await wait()
console.log(data)
return (
<>
<h3>Title 1</h3>
<Suspense fallback={<div className="text-red-600">Loading...</div>}>
<p>some delayed text</p>
</Suspense>
<h3>Title 2</h3>
<Suspense fallback={<div className="text-red-600">Loading2...</div>}>
<p>some delayed text</p>
</Suspense>
</>
)
}
export default TestRoute14 Replies
Are you expecting the
Because what you are showing here is a page which instantly calls a promise - resulting in block time on page render.
If you want to use the Suspense like you are trying to do here, you will have to make that async call in another component, which is then wrapped in the
<p>some delayed text</p> to be delayed?Because what you are showing here is a page which instantly calls a promise - resulting in block time on page render.
If you want to use the Suspense like you are trying to do here, you will have to make that async call in another component, which is then wrapped in the
Suspense like:export default function Page() {
return (
<Suspense>
<SomeServerComponent />
</Suspense>
)
}
export async function SomeServerComponent() {
await wait();
return (
<p>some delayed text</p>
)
}Segugio ItalianoOP
What about this approach
If I have to fetch the data in the parent only?
@Paul.
import React, { Suspense } from "react"
import Await from "./await"
export const dynamic = "force-dynamic"
const wait = async () => {
let data = {
data: "some data",
}
await new Promise((resolve) => setTimeout(resolve, 2000))
return data
}
const TestRoute = async () => {
let promise = wait()
return (
<>
<h3>Title 1</h3>
<Suspense fallback={<div>Loading....</div>}>
<Await promise={promise}>{({ data }) => <p>{data}</p>}</Await>
</Suspense>
</>
)
}
export default TestRoute interface AwaitProps<T> {
promise: Promise<T>
children: (value: T) => JSX.Element
}
const Await = async <T,>({ promise, children }: AwaitProps<T>) => {
let data = await promise
return children(data)
}
export default AwaitIf I have to fetch the data in the parent only?
@Paul.
You mean your
Await component doesn't have access to the promise?Nile Crocodile
Try with
await use(promise) in your AwaitIt might need to run on the client
@Nile Crocodile Try with `await use(promise)` in your `Await`
The entire idea of the OP was to stream the component, by turning it into a client component that kinda defeats the purpose right?
Nile Crocodile
The data will be streamed, but it will show the
Suspense fallback until it ready.. Otherwise it will need to wait for the server..The loading.tsx is for the entire page, whereas they want to show a loading a specific place.
Yeah I got that part - so they shouldn't need the
use. Just the await promise() should be enough for streaming to workNile Crocodile
Without
use I think it will block the render until it have been resolved which means the fallback will not show.Segugio ItalianoOP
the approach I posted above kind of working. But one issue like somehow data is cached. If I am coming to this route then the for the first time loading state is perfectly working but next time I am navigating to this route the data is directly shown.
How can I change this behaviour if I need fresh data always?
How can I change this behaviour if I need fresh data always?
You can use
revalidation https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating. But that only works on page-level not on component-level.Segugio ItalianoOP
ok thanks then in my case, I can use like this
and then use directly in the component?
import { GET_PROFILE } from "../../../graphql/queries"
const fetchProfile = async () => {
const response = await fetch(process.env.URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
query Profile {
profile {
id
name
}
}`,
}),
next: { revalidate: 0 },
})
const data = await response.json()
return {
profile: data?.data?.profile || null,
}
}
export default fetchProfileand then use directly in the component?
that should work - yes