skip to Main Content

I am using the next.js new functionality to render content using SSR, I am making component async as docs says.

So this is my simple page component

export default async function Home() {

    const res = await fetch("http://localhost:3000/api/get-quotes");
    const quotes = await res.json();

    return (
        <main className={styles.main}>
            <h1>Hello Visrut</h1>
            <div>
                <span>Total quotes: {quotes.length}</span>
            </div>
        </main>
    )
}

I have authenticated and non-authenticated routes in my application, what I am doing to separate them in _app.tsx

// _app.tsx
interface AppProps {
  Component: React.ElementType;
  pageProps: Record<string, unknown>;
}

const App: React.FC<AppProps> = ({ Component, pageProps }) => {
  const router = useRouter();

  if (router.pathname.includes("home")) {
    return <Home />;   // Error: 'Home' can't be used as a JSX component, Its return type Promise<Home> is not a valid JSX component.
  }

  return (
    <AuthContextProvider>
      <Navbar />
      <Head />
      <Component {...pageProps} />
      <Footer />
    </AuthContextProvider>
  )
};

export default App;

I want to render the Home component which does not require authentication, it seems like I can’t just directly put it as children also because of the async keyword.

I am also getting this error on the browser while directly rendering async component into other normal components in next.js

Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

2

Answers


  1. Move your async code into getServerSideProps function and pass the response into your Home component as Props

    Next 12

    Reference: https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props

    export async function getServerSideProps() {
      const res = await fetch("http://localhost:3000/api/get-quotes");
      const quotes = await res.json();
    
      return {
        props: {
          quotes: quotes,
        }, // will be passed to the page component as props
      };
    }
    
    export default function Home({quotes}) {
        return (
            <main className={styles.main}>
                <h1>Hello Visrut</h1>
                <div>
                    <span>Total quotes: {quotes.length}</span>
                </div>
            </main>
        )
    }
    

    Next 13

    import { use } from "react";
    
    async function getQuotes() {
      return await fetch("http://localhost:3000/api/get-quotes", {
        cache: "no-store",
      }).json();
    }
    
    export default function Home() {
      const quotes = use(getQuotes());
      return (
        <main className={styles.main}>
          <h1>Hello Visrut</h1>
          <div>
            <span>Total quotes: {quotes.length}</span>
          </div>
        </main>
      );
    }
    
    
    Login or Signup to reply.
  2. You have some concept issues in your code

    You cannot have async components like that, the components must be sync functions, to fix this you have some ways to do it

    Using useEffect:
    You can your fetch calls inside an effect that handles these asynchronous issues, just like this:

    import { useState, useEffect } from 'react';
    
    export default function Home() {
        const [quotes, setQuotes] = useState([])
        const [loading, setLoading] = useState(true)
    
        useEffect(() => {
          const getData = async () => {
            const res = await fetch("http://localhost:3000/api/get-quotes");
            const data = await res.json();
            setQuotes(data)
            setLoading(false)
          }
          getData();
          return () => {
            // here you can clean the effect in case the component gets unmonth before the async function ends
          }
        },[])
    
        if (loading) {
          return <>loading...</>
        }
    
        return (
            <main className={styles.main}>
                <h1>Hello Visrut</h1>
                <div>
                    <span>Total quotes: {quotes.length}</span>
                </div>
            </main>
        )
    }
    

    Using the SSR from Next look at https://nextjs.org/docs/basic-features/data-fetching/overview here you can see some options to fetch the data before render the page, that is great, an example here:

    import { useRouter } from 'next/router'
    
    export default function Home({data}) {
        const router = useRouter();
    
        if (!router.isReady) {
          return <>loading...</>
        }
    
        return (
            <main className={styles.main}>
                <h1>Hello Visrut</h1>
                <div>
                    <span>Total quotes: {data.length}</span>
                </div>
            </main>
        )
    }
    
    export const getServerSideProps = async () => {
      const res = await fetch("http://localhost:3000/api/get-quotes");
      const data = await res.json();
    
      return {
        props: {
          data,
        },
      }
    }
    
    

    That will fix the issue you mentioned, but you also have a concept issue with the _app file, the way you want to restrict the navigation, it looks like you want the layout (navbar, footer, etc) to only render when it’s not home, so that would be something like this:

    const App: React.FC<AppProps> = ({ Component, pageProps }) => {
      const router = useRouter();
    
      if (router.pathname.includes("home")) {
        return <Component {...pageProps} />
      }
    
      return (
        <AuthContextProvider>
          <Navbar />
          <Head />
          <Component {...pageProps} />
          <Footer />
        </AuthContextProvider>
      )
    };
    
    

    However, this only restricts the visual that it shows according to each route, here you are not really validating if you have an active session, but hey, I hope it helps you!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search