reactjsnext.jsreact-suspense

Nextjs Suspense won't work with fetch api but with new promise timeout work perfectly


Next.js doesn't work, where I try take data using fetch Api but won't display suspense fallback but when I write new promise with setTimeout it's work perfectly. As I know fetch API is based on promises, so why without adding additional line of code with promise suspense won't work?

Example:

interface Post {
    userId: number,
    id: number,
    title: string,
    body: string
}


async function getPosts() {
    const posts: any = await fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json()).catch(res => new Error('Failed to fetch data'));
    // await new Promise((resolve) => setTimeout(resolve, 2000));
    return posts;
}

async function View() {
    const posts: Post[] = await getPosts();

    
    return (
        <main>
            {posts && posts.map((post: Post) => {
              return <li>{post.title}</li>  
            })}
        </main>
    )
}

export default View

Parent component:

import { Suspense } from "react"
import Loading from "./loading"
import View from '../../components/View';



const Dashboard = () => {
  return (
    <div>
        <Suspense fallback={<Loading />}>
            <View />
        </Suspense>
    </div>
  )
}

export default Dashboard

Try find specified information but still is confusing to understand.


Solution

  • The issue may be related to the handling of promises. There is no need to use setTimeout to manage promises. If you handle promises using async/await, the code waits until data is received into any variable. When waiting for data, the issue might be associated with the fact that Suspense is currently not supported for server-side rendering (SSR) in Next.js. Suspense is primarily designed for use with React's concurrent mode, which is not fully compatible with server rendering.

    1. Use useEffect to Fetch Data which is common method to fetch data after component mounted

    import { useEffect, useState } from 'react';
    
    async function getPosts() {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts');
      const posts = await response.json();
      return posts;
    }
    
    function View() {
      const [posts, setPosts] = useState([]);
    
      useEffect(() => {
        getPosts().then((data) => setPosts(data));
      }, []);
    
      return (
        <main>
          {posts.map((post) => (
            <li key={post.id}>{post.title}</li>
          ))}
        </main>
      );
    }
    
    export default View;
    

    "if you are using in SSR then use 'use client' for it .

    2. Ensure that Fallback Component <Loading /> component is working correctly and doesn't have any issues that could prevent it from being displayed.

    Document refer React Suspense

    3. If You are working on SSR ( Server side Rendering ) Nextjs Then try to use getServerSideProps or getStaticProps for fetching data.

    import View from '../components/View';
    
    const Dashboard = ({ posts }) => {
      return (
        <div>
          <View posts={posts} />
        </div>
      );
    };
    
    export async function getServerSideProps() {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts');
      const posts = await response.json();
    
      return {
        props: {
          posts,
        },
      };
    }
    
    export default Dashboard;