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

Suspense rendering problems with Next.js #3501

Closed
1 task done
filipjakov opened this issue Mar 30, 2022 · 8 comments
Closed
1 task done

Suspense rendering problems with Next.js #3501

filipjakov opened this issue Mar 30, 2022 · 8 comments

Comments

@filipjakov
Copy link

filipjakov commented Mar 30, 2022

  • Check if updating to the latest Preact version resolves the issue

Using Next.js (SSR) -> package.json:

"next": "12.1.2",
"next-plugin-preact": "3.0.6",
"preact": "10.7.0",
"preact-render-to-string": "5.1.20",
"react": "npm:@preact/[email protected]",
"react-dom": "npm:@preact/[email protected]",

Describe the bug

Note the different html elements, styles applied on them and how they get rendered. The Content component just does a SWR api call with 2s sleep so suspense can get triggered.

isClient is used to use suspense once on the client since (afaik) Suspense does not work on the server.

  1. Same html element:

Let's say I have the following content on the page:

{isClient() ? (
	<Suspense fallback={<h1 style={{ color: 'red' }}>Fallback</h1>}>
		<Content>
			<h1 style={{ color: 'blue' }}>Content</h1>
		</Content>
	</Suspense>
) : (
	<h1 style={{ color: 'green' }}>Else</h1>
)}
  • retrieved from the server: "Else" colored green (as expected)
  • rendered while loading: "Else" colored in green (???)
  • rendered when suspense done: "Content" colored in green (???)
  1. Different html elements:
{isClient() ? (
	<Suspense fallback={<h1 style={{ color: 'red' }}>Fallback</h1>}>
		<Content>
			<h2 style={{ color: 'blue' }}>Content</h2>
		</Content>
	</Suspense>
) : (
	<h3 style={{ color: 'green' }}>Else</h3>
)}
  • retrieved from the server: "Else" colored green, h3 tag (as expected)
  • rendered while loading: "Else" colored green, h3 tag (???)
  • rendered when suspense done: "Content" colored blue, h2 element (as expected), but there is also "Else" colored green, h3 tag (???) in the DO

Bugs

  • if the same html element is used as content, preact suspense just patches the existing html element (from the else part) which applies wrong html properties (style, classname,...)
  • if different html elements are used, the "else" part persists in the DOM
  • suspense fallback never renders

Expected behavior
Suspense should work as expected: should render the fallback & should re-render(?) the consuming component once the suspended request is finished.

@filipjakov filipjakov changed the title Suspense fallback rendering problems with Next.js Suspense rendering problems with Next.js Mar 30, 2022
@alex289
Copy link

alex289 commented Mar 30, 2022

I have some issues with NextJs too. When I try to build I get the error: Can't find preact/compat/client
Can you successfully build your project?

@filipjakov
Copy link
Author

@alex289 yes, no build problems on my side

@marvinhagemeister
Copy link
Member

@alex289 can you open a new issue for that? It seems like the error you're seeing is not related to the issue here.

@JoviDeCroock
Copy link
Member

I think this issue presents itself because we stay in hydration mode where we don't patch missmatches in DOM-attributes as this is commonly seen as a bug between Server and Client.

So DOM arrives from server as

<h3 style={{ color: 'green' }}>Else</h3>

then we switch to hydrate() and enter the isClient() branch, this suspends which makes us get

<h1 style={{ color: 'red' }}>Fallback</h1>

We hydrate but don't patch the innerText nor the style, nor the nodeType. When the Suspense resolves we see that the DOM still remains the same on

<h2 style={{ color: 'blue' }}>Content</h2>

We yet again haven't patched any of the needed things, I think this is because of our deferred hydration tricks we use when we are revolving around Suspending boundaries because we stay in hydration mode even though we will have to patch the nodeType, ... ideally

WDYT @marvinhagemeister @developit

@developit
Copy link
Member

Is this type of client/server variance actually valid? React is likely destroying and recreating the tree here, which doesn't seem like a good point of reference for correctness. We could flip hydration-deferred subtrees into mutative hydration mode, but that's a performance drawback.

@JoviDeCroock
Copy link
Member

JoviDeCroock commented Apr 5, 2022

Well I guess that's a valid point when SSR returns vastly different content but if we never escape deferred hydration mode and switch route this could occur as well right? i.e. different lazy route can result in the content getting hydrated and being a different Component all together

@JoviDeCroock
Copy link
Member

Coming back to this @developit I do think React warns for this and that Lighthouse gives you CLS for this because we are changing the SSR response.

I think what they advice in React is to do

  • hydrate
  • rerender after hydrate in an effect
  • updated UI

@JoviDeCroock
Copy link
Member

In general this is considered a hydration mismatch though as the SSR'd result will be different from the client result at all times. This is present in React as well as Preact, referring to #4442 for explanations. Basically you'd have to have a isHydrated check rather than isClient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants