Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not able to fetch data from Client Component (infinite fetch() calls) #42180

Closed
1 task done
AlexLup06 opened this issue Oct 30, 2022 · 40 comments
Closed
1 task done

Not able to fetch data from Client Component (infinite fetch() calls) #42180

AlexLup06 opened this issue Oct 30, 2022 · 40 comments
Assignees
Labels
area: app App directory (appDir: true)

Comments

@AlexLup06
Copy link

AlexLup06 commented Oct 30, 2022

Edit by @acdlite: This is mostly a React issue. We're working to address it: #42180 (comment)


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

@AlexLup06 AlexLup06 added the bug Issue was opened via the bug report template. label Oct 30, 2022
@apostolos
Copy link
Contributor

Hi @AlexLup06,

The problem is with your client component. Async components are only supported on the server. If you remove the async keyword from Fact it should work fine.

You also need to remove the pages folder, that should allow Next to automatically create a layout.tsx in the app dir. It can't work without a root layout.

I forked your sandbox, you can find all these changes along with a working example here: https://codesandbox.io/s/next-appdir-use-i43p25

@AlexLup06
Copy link
Author

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().

@apostolos
Copy link
Contributor

Hi @AlexLup06,

This is an obvious bug in the docs. I left a comment.

Per React's RFC, async is only reserved for server components: https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise

@apostolos
Copy link
Contributor

@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

@AlexLup06
Copy link
Author

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

@balazsorban44 balazsorban44 added the area: app App directory (appDir: true) label Oct 31, 2022
@palmer-cl
Copy link

Im also having the same infinite fetch call issue.

@zenflow
Copy link
Contributor

zenflow commented Nov 3, 2022

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

@palmer-cl
Copy link

Im confused on how there can be such a broken workflow in the docs as an example...

@zenflow
Copy link
Contributor

zenflow commented Nov 3, 2022

Yeah, same here to be honest. I know it's beta but considering it's such a fundamental thing and not a detail or edge-case, it is surprising

@zenflow
Copy link
Contributor

zenflow commented Nov 3, 2022

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

I found out how: #41745 (comment)

@ShravanSunder
Copy link

ShravanSunder commented Nov 4, 2022

I'm having the same issue with infinite fetch in use

Using cache has an error on the server side


Error: Not implemented.
    at Object.getCacheForType (/Users/shravansunder/Documents/dev/projects/2022-09-dpatron-experiments/node_modules/next/dist/compiled/react-dom/cjs/react-dom-server.browser.development.js:7208:9)
    at /Users/shravansunder/Documents/dev/projects/2022-09-dpatron-experiments/node_modules/next/dist/compiled/react/cjs/react.development.js:1644:28
    at Component (webpack-internal:///(sc_client)/./src/app/TestComponent.tsx:25:62)
    at renderWithHooks (/Users/shravansunder/Documents/dev/projects/2022-09-dpatron-experiments/node_modules/next/dist/compiled/react-dom/cjs/react-dom-server.browser.development.js:7629:16)
    at renderIndeterminateComponent (/Users/shravansunder/Documents/dev/projects/2022-09-dpatron-experiments/node_modules/next/dist/compiled/react-dom/cjs/react-dom-server.browser.development.js:7702:15)

@balazsorban44 balazsorban44 added kind: bug and removed bug Issue was opened via the bug report template. labels Nov 4, 2022
@balazsorban44
Copy link
Member

Thanks for raising this issue. A few things here. The original problem can be resolved as outlined in #42180 (comment), thanks @apostolos for helping out!

But either way, I first did it the same way you did but would then face the problem of infinite fetch calls.

However the infinite fetch should probably not happen, so I will keep this issue open to track it.

Why do the docs have an async on the client components?

I also scanned the docs, and cannot find where we document async client components, if someone can link me to it, I can make sure we fix it!

@zenflow
Copy link
Contributor

zenflow commented Nov 4, 2022

I also scanned the docs, and cannot find where we document async client components, if someone can link me to it, I can make sure we fix it!

@balazsorban44 here it is https://beta.nextjs.org/docs/data-fetching/fetching#example-fetch-and-use-in-client-components

@balazsorban44
Copy link
Member

"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? 🤔

@zenflow
Copy link
Contributor

zenflow commented Nov 4, 2022

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..

@palmer-cl
Copy link

@balazsorban44 Is there a suggested workaround for the infinite fetch issue?

@balazsorban44
Copy link
Member

The current workaround is described under the above-mentioned link:

For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as SWR. Alternatively, you can wrap fetch in an utility function outside of the component, and call the function inside the use hook.

@palmer-cl
Copy link

@balazsorban44 Im doing that currently and still getting the infinite fetching. See the utility function.

With code:

'use client'
import { use } from 'react'

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

export default function Impact() {
  const data = use(getData())

  return (
  <>
    <div>Hello</div>
  </>)
}

image

@AlexLup06
Copy link
Author

@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()

@ShravanSunder
Copy link

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

@AlexLup06 AlexLup06 changed the title Not able to fetch data from Client Component Not able to fetch data from Client Component (infinite fetch() calls) Nov 4, 2022
@apostolos
Copy link
Contributor

@balazsorban44

I also scanned the docs, and cannot find where we document async client components, if someone can link me to it, I can make sure we fix it!

The original docs indeed had async client components, that's why many people were confused. I reported it via the inline tool and it was fixed the same day, just a few hours later (that was 5 days ago).

@apostolos
Copy link
Contributor

@palmer-cl every time React calls your render function, the getData() inside use() returns a new Promise. It's not the same Promise that the one that got used to Suspend originally. The RFC explains this problem in detail: https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise

Please this comment for a workaround #42180 (comment)
The real fix is to wrap getData with cache()

@palmer-cl
Copy link

palmer-cl commented Nov 4, 2022

@apostolos cache does not take a Promise. See below:

image

@apostolos
Copy link
Contributor

@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 (<>...</>);
}

@palmer-cl
Copy link

That did it. Thanks @apostolos

@ShravanSunder
Copy link

@apostolos Using cache as you suggested gives server errors:

Uncaught Error: Not implemented.
at updateDehydratedSuspenseComponent (react-dom.development.js?f8d2:23546:1)
at updateSuspenseComponent (react-dom.development.js?f8d2:23243:1)
at beginWork (react-dom.development.js?f8d2:24552:1)
at beginWork$1 (react-dom.development.js?f8d2:31625:1)
at performUnitOfWork (react-dom.development.js?f8d2:30557:1)
at workLoopSync (react-dom.development.js?f8d2:30442:1)
at renderRootSync (react-dom.development.js?f8d2:30395:1)
at performConcurrentWorkOnRoot (react-dom.development.js?f8d2:29670:1)
at workLoop (index.js?349c:10:3922)
at flushWork (index.js?349c:10:3630)
at MessagePort.performWorkUntilDeadline (index.js?349c:10:1812)

@apostolos
Copy link
Contributor

@ShravanSunder are you on v13.0.2?

@AlexLup06
Copy link
Author

@apostolos
For me its not working too even tho I am an v13.0.2
Same error as @ShravanSunder

What version of React is required? I am on 18.2.0

@apostolos
Copy link
Contributor

@AlexLup06 should work fine with anything from 18.2.0 and above or with react@next, react@experimental.

I made a separate sandbox to test cache() https://codesandbox.io/s/next-appdir-use-cache-xksk50?file=/app/fact.tsx

You'll notice that it works correctly. It makes a single fetch on the client and does not go into an infinite loop.

However an error shows up with the message "Not implemented.". This is coming from the server and it happens because some parts of caching are still WIP: https://github.com/facebook/react/blob/8e2bde6f2751aa6335f3cef488c05c3ea08e074a/packages/react-server/src/ReactFizzCache.js

@AlexLup06
Copy link
Author

@apostolos
Gotcha!
Yeah I saw that everything on the client worked so far but thought I was missing something for the server to render correctly.

@ShravanSunder
Copy link

@apostolos i'm running react@next and next@latest. I do get the not implemented error, does that mean it shouldn't be used currently?

@SimonProper
Copy link

SimonProper commented Nov 7, 2022

I had the same issue with the infinity loop, @himyjan provided a link to another workaround in #42265 (comment)

@jescalan
Copy link
Contributor

jescalan commented Nov 8, 2022

👋 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!

@songhobby
Copy link

songhobby commented Dec 1, 2022

@jescalan

Using lodash.memoize to cache the data fetching function works

example

”use client";

import { use } from 'react';
import _ from ‘lodash’;

const getData = _.memoize( async () => {
  const res = await fetch('https://api.example.com/...');
  return res.json();
});

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

  return '...';
}

@deviusboy
Copy link

Hello! I'm still learning about Next13. In my opinion, I found an option how to get rid of the error described in this issue. Take out all the logic of the "use client" client component into a separate component and take it out into a separate file and folder, let's say Components... Everything related to the state and other hooks that you need. After I took it out everything worked as expected. Moreover, the asynchronous function waiting for data makes a request directly to the database on the server side.

Here is an example of my code. Notice the "use client" comment. It is this component that is the client component.

import s from './personal.module.scss'
import {cookies} from 'next/headers'
import {redirect} from 'next/navigation';
import React from "react"

const {verify} = require('jsonwebtoken')

import Calculation_Prog from "../../Components/calculationProg/calculation_prog";
import reqProjects from "../../db/projects";

const {UserProjects} = require('../../db/mongo/schemes.js')

let getProject = async () => {

if (!cookies().get('jwt')) return redirect('/')

const JWT = cookies().get('jwt')

const parseJWT = verify(JWT.value, 'Jetta')

const projects = await UserProjects.find({email: parseJWT.email}).exec()

return projects

}

export default async function Personal() {

const projects = await getProject()

return (
    <div className={s.containerMain}>
        {projects.map((item, key) => {
            return (

                <ItemPoroject key={key} item={item}/>
            )
        })}
    </div>)

}

function ItemPoroject({item}) {

const projectsDB = reqProjects()

return (

    <div className={s.containerCalc}>
        <Calculation_Prog                                                  //'use client'
            name={projectsDB[item.nameProject].name}
            projectName={item.nameProject}
            descriptions={projectsDB[item.nameProject].descriptions}
            width={item.width}
            height={item.height}
            length={item.length}
            square={item.length * item.width}
            region={item.city}
            price={item.price}
        />
       
    </div>
)

}

@studentIvan
Copy link

The infinity fetch happens even without the use(). Any mistake on fetch, e.g. 400 status code === infinity fetches on the client side.

When I've added the .catch(() => null) block to the end of fetch, It stopped, before the hot reloader reloaded it...
Huge bug

@iSuslov

This comment was marked as spam.

@songhobby
Copy link

songhobby commented May 7, 2023

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

@acdlite
Copy link
Contributor

acdlite commented May 10, 2023

As explained by @apostolos, async/await on the client is not yet supported. We have rough plans to support this in the future, and as some of you have discovered, if you use it in a particular way, it can work. But the experience is still very rough so it's not recommended. In the meantime, you should prefer to do as much data fetching as possible in Server Components, and use an established library like SWR for any leftover client cases.

I agree that the DX is not great if you accidentally use async/await on the client — we need to fix that in React with better loop detection and warnings if this happens. So I consider this mostly a React issue, not a Next.js one. However we can also do some stuff on the Next.js side to make this less of a footgun, like a lint rule perhaps.

I'm going to lock this thread for now but leave the issue open so people know we (the React and Next.js teams) are working on it!

@vercel vercel locked and limited conversation to collaborators May 10, 2023
@acdlite
Copy link
Contributor

acdlite commented May 10, 2023

Actually let's just track this in the React tracker: facebook/react#26801

@acdlite acdlite closed this as completed May 10, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: app App directory (appDir: true)
Projects
None yet
Development

No branches or pull requests