vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.3k stars 27.02k forks source link

Unable to use imported objects from client components in server components #71369

Open neefrehman opened 1 month ago

neefrehman commented 1 month ago

Link to the code that reproduces this issue

https://stackblitz.com/edit/stackblitz-starters-xotxrf?file=app%2Fpage.tsx

To Reproduce

  1. Start the application in Development
  2. Check the logs for a log of test string that gets imported from the ClientComponent.tsx
  3. Notice that instead of seeing the string, you see null
  4. Notice that, if the console.log on line 4 of page.tsx is commented and then uncommented, you do see the test string in the logs on the first render

Current vs. Expected behavior

Currently, It looks like imports from client components into server component are proxied. I would expect to be able to import and use an object or other primitive from a client component, in any other component.

This bug was previously reported in https://github.com/vercel/next.js/issues/66212, and supposedly fixed in https://github.com/vercel/next.js/pull/66990, but from testing it seems to still be a problem, both with and without turbopack.

From the issue that previously reported this:

This used to work prior to Next.js 14 (13.5.6) but for some reason doesn't work now. I have an inkling as to why (bundling and wanting to separate the interweaving of client and server components) but this break a pattern we use for colocating queries (GraphQL, GROQ etc.) with our components.

If anybody can point me to somewhere in the Next.js documentation explaining this behavior I would appreciate it as right now this type of bundling breaks everything I expect out of ES Modules.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.0.0: Mon Aug 12 20:52:12 PDT 2024; root:xnu-11215.1.10~2/RELEASE_ARM64_T6020
  Available memory (MB): 32768
  Available CPU cores: 10
Binaries:
  Node: 20.18.0
  npm: 10.8.2
  Yarn: 1.22.22
  pnpm: 9.12.1
Relevant Packages:
  next: 15.0.0-rc.1
  eslint-config-next: 15.0.0-rc.1
  react: 19.0.0-rc-83825814-20241015
  react-dom: 19.0.0-rc-83825814-20241015
  typescript: 5.6.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Runtime, Turbopack

Which stage(s) are affected? (Select all that apply)

next dev (local), next build (local), Vercel (Deployed)

Additional context

I understand the fix has been reverted from 14 due to it being a breaking change, but I'm expecting it to be there in canary.

mohsen-salehi commented 1 month ago

This process includes the following steps:

Server Rendering: The Server Component is rendered on the server and produces HTML content. Hydration: After the HTML is sent to the browser, the Client Components are activated and React "hydrates" them. This means that React allows the Client Component to respond to user state and events. As a result, you can put the Client Component in the Server Component, but you cannot transfer the internal data of the Client Component to the Server Component, because they are running in two different environments.


And how can you send an html file to the server??? When you want to do this, you have a way to either make a request to the route or send it to the react core to render it!‌

samcx commented 1 month ago

@neefrehman I am not seeing null in development.

CleanShot 2024-10-17 at 15 05 15@2x

neefrehman commented 1 month ago

@samcx interesting! I can repro that locally and do indeed see the string. On stackblitz it's a different story:

377607789-0a63ed4f-4abd-434e-9235-29860fdc589b

Though, I now guess the repro is a bit too minimal, as the string is visible in the server log, but at runtime it seems to still be a proxied object as opposed to the "real" exported one. I think this is shown by the stackblitz logs above, or the [Function: testString] log that should be visible in the terminal locally.

When we try to do anything other than log the imported object, we are unable to do so. My use case is similar to the OP of #66212's, using graphql fragments—that are colocated with their component—in another document that may be constructed on the server. The error we see is: TypeError: f.definitions is not iterable, where f should be the imported grapqhl fragment.

It strikes me that this may be related to #51593, and this proxying is also what throws the below error if you tried to do testString.length on the server:

Error: Cannot access testString.length on the server. You cannot dot into a client module from a server component. You can only pass the imported name through.

Is this behaviour documented anywhere? From my understanding Next supports ESM, which would mean these imported objects not being proxied. At least, that's what I initially understood the fix from #66990 to point towards.