next.js: Not able to fetch data from Client Component (infinite fetch() calls)

Edit by @acdlite: This is mostly a React issue. We’re working to address it: https://github.com/vercel/next.js/issues/42180#issuecomment-1542434389


Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000
Binaries:
  Node: 16.14.2
  npm: 8.5.0
  Yarn: 1.22.18
  pnpm: N/A
Relevant packages:
  next: 13.0.0
  eslint-config-next: 13.0.0
  react: 18.2.0
  react-dom: 18.2.0

What browser are you using? (if relevant)

Version 106.0.5249.119

How are you deploying your application? (if relevant)

next dev

Describe the Bug

I was just playing around getting to know next 13 when I tried fetching data from a Client component. I literally just copy pasted the example from the docs: https://beta.nextjs.org/docs/data-fetching/fetching#example-fetch-and-asyncawait-in-server-components The Client Component is used inside page.tsx where its wrapped in <Suspense fallback={<div>Loading...</div>}></Suspense>

When running that I get:

  • 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.
  • TypeError: Cannot read properties of null (reading ‘use’)

Server Component:

import { Suspense } from "react";
import Fact from "./fact";

export default function Home() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <Fact />
      </Suspense>
    </div>
  )
}

Client Component:

"use client";

import { use } from 'react';

async function getData() {
    console.log("fetch")
    const res = await fetch('https://catfact.ninja/fact');
    // The return value is *not* serialized
    // You can return Date, Map, Set, etc.
    return res.json();
}

export default async function Fact() {
    const name = use(getData());

    return (
        <div>
            {name.fact}
        </div>
    );
}

Expected Behavior

The root server component in page.tsx should render right away and the client component should render when the data was fetched. Before the data is fetched it should say Loading….

Link to reproduction

https://codesandbox.io/s/reverent-danilo-zgle45?file=/app/fact.tsx

To Reproduce

Open the link or copy paste the code inside your app

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 5
  • Comments: 40 (19 by maintainers)

Most upvoted comments

👋 Hi friends! Really appreciate all the investigation and discussion here. I spoke with the team about this and it looks like, perhaps as expected, we don’t have this functionality quite finished yet. We tried to clarify in the docs that fetch is not yet supported in client components - the recommendation right now is to fetch data in server components.

This is a feature that will be supported in the future for sure, but this is a beta release and we’re not quite there yet. Thank you so much for testing out the new app directory, and I’ll update you all here once we do support fetching in client components!

The infinite use() client error and cache server error are roadblock to get next 13 to work

Am I missing something? 🤔

@balazsorban44 No, I don’t think so. My mistake. There are no async client components in docs that I’m aware of. My apologies…

Hi @apostolos Why do the docs have an async on the client components? But either way, I first did it the same way you did but would then face the problem of infinite fetch calls.

Bildschirmfoto 2022-10-30 um 17 25 01 (2)

I would not stop calling the getData() function. When you remove the fetch() and the fakeDelay() it is being called 10000 times per second. Might there be something wrong with use().

Nextjs 13 is now stable with

INFINITE

LOOP

BUG

@balazsorban44 The async on the client component was already changed. But there really was one.

The problem actually exactly happens when calling the function with the fetch in use()

@iSuslov Please be nice to the people working on open source project. Our company is using Next.js 13 and it is amazing!

That did it. Thanks @apostolos

@palmer-cl cache() takes a function declaration as the only argument. You are not supposed to call it inside render.

See the commented code in my example: https://codesandbox.io/s/next-appdir-use-i43p25?file=/app/fact.tsx

Try this:

const getData = cache(async () => {
  const res = await fetch('https://v2.jokeapi.dev/joke/Any?safe-mode');
  return res.json();
});

Then, inside render call the wrapped function:

export default function Impact() {
  const data = use(getData());
  return (<>...</>);
}
"use client";

import { use } from 'react';

async function getData() {
  const res = await fetch('https://api.example.com/...');
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
  return res.json();
}

export default function Page() {
  const name = use(getData());

  return '...';
}

I cannot see async on the exported Page component. Am I missing something? 🤔

The cache() function seems to be already available in react 18.2.0 somehow…

Anyways I tried it and it fixed the infinite fetch issue on client side, but it throws a “Not implemented” error on server. (Remember client components are also rendered on server.)

Related issue: #41852

@apostolos Thank you! So for now we would have to use this (nasty) work around with the double render until they solve it?

@AlexLup06 regarding the infinite calling of fetch, based on the RFC again, the promise passed to use() is supposed to be cached (stable identity between renders).

There is a cache() function that we can use in the experimental React release but it’s not implemented yet.

I’ve updated my example (https://codesandbox.io/s/next-appdir-use-i43p25?file=/app/fact.tsx) with how it is supposed work (commented out) plus a simple workaround to avoid infinite fetches with a simple caching of the fetch Promise. It’s not correct due to the fact that the cache on the server and the cache on the client are not the same, therefore you get different results from the server render and from the client render.

Soon, Next.js will be able to pass the resolved promise from the server to the client somehow. That should avoid double-rendering (the server result will be used). You can read more about this in the RFC: https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#passing-a-promise-from-a-server-component-to-a-client-component