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

defer never resolving when hydration errors are present #5165

Closed
DerJacques opened this issue Jan 20, 2023 · 10 comments
Closed

defer never resolving when hydration errors are present #5165

DerJacques opened this issue Jan 20, 2023 · 10 comments

Comments

@DerJacques
Copy link

DerJacques commented Jan 20, 2023

What version of Remix are you using?

1.11.0

Steps to Reproduce

This simple page causes the issue to occur on my end:

import { defer } from '@remix-run/node'
import { Await, useLoaderData } from '@remix-run/react'
import { Suspense } from 'react'

export const loader = async () => {
  const longRunningPromise = new Promise((resolve) => {
    setTimeout(
      () =>
        resolve({
          result: 42
        }),
      2000
    )
  })

  return defer({
    fastData: "I'm already here",
    longRunningPromise
  })
}

export default function Page() {
  const data = useLoaderData()

  return (
    <div>
      Available on first render: {data.fastData}
      <Suspense fallback={<p>Slow...</p>}>
        <Await resolve={data.longRunningPromise} errorElement={<div>Something went wrong</div>}>
          {(longRunningPromise) => <div>Answer: {longRunningPromise.result}</div>}
        </Await>
      </Suspense>
    </div>
  )
}

Refreshing the page will sometimes work, and sometimes the loading state will stay forever.
This appears to be linked to browser extensions and hydration, as the error disappears entirely when running the test in an incognito window.

Expected Behavior

After about 2 seconds, I would expect the long running promise in the above example to resolve and be displayed on the page.

Actual Behavior

The promise is only resolved sometimes.
When the promise is not resolved, the console shows a client side hydration error.

I've seen this hydration many times in the past, and it appears to be related to my browser extensions (namely React Devtools): https://remix.run/docs/en/v1/pages/gotchas#browser-extensions-injecting-code

So the hydration error is not new, but whenever I see it, the promise ends up never resolving.

Below is a video showing the problem in a fresh install of Remix (the "Indie stack"):

Screen.Recording.2023-01-20.at.16.55.44.mov

Note: This may be related to: #5153
But I don't think this is specific to Prisma.

@clgeoio
Copy link

clgeoio commented Jan 24, 2023

I've created a proposal / discussion for this issue #5244 as there's quite a few problems stemming from this same cause

@WesleyYue
Copy link

To be clear, I think there are 3 separate issues here:

  1. Allow `head` and `body` to be hydrated separately #5244 Extensions that inject code causes hydration errors
  2. "This Suspense boundary received an update before it finished hydrating."
  3. This issue: "defer doesn't resolve if there are hydration errors"

1 and 2 are different ways that hydration can fail, which triggers 3.

@gaearon
Copy link

gaearon commented Feb 23, 2023

We posted a little update on possibly related issues in facebook/react#24430 (comment). Please see a few comments below, as well. TLDR: we would not recommend hydrating head/body separately as a long-term strategy.

@Ehesp
Copy link

Ehesp commented Mar 17, 2023

Yikes this one just caught me out in prod (Remix + Vercel) 😅 Ended up using Disable Extensions Temporarily to spot it was Chrome Extensions making it error and ended up here.

@DerJacques
Copy link
Author

Hi @MichaelDeBoey
I can see you marked this issue as closed, but as far as I know it continues to be an ongoing issue.

Since the issue of hydration errors depends so much on the browser extensions and configuration of end-users, I simply don't currently see a way of using defer with Remix. I understand that the root of the problem may be related to issues in React, but in that case I would argue that defer simply isn't ready to be used in Remix and should be advertised as being experimental.

Can you shed some more light on the issue?

@Ehesp
Copy link

Ehesp commented Sep 7, 2023

@DerJacques I "solved" this by upgrading react and react-dom to the next tag (although using fixed version), see https://www.npmjs.com/package/react?activeTab=versions

    "react": "18.3.0-canary-21a161fa3-20230609",
    "react-dom": "18.3.0-canary-21a161fa3-20230609",

Although that caused issues with some other packages having dependency constrain on 18.2 - so to get around this I npm install with --force (or add force=true to .npmrc). React is pretty great at backwards compatibility, so I've not seen any issues so far.

@DerJacques
Copy link
Author

Thanks a lot @Ehesp! It's really nice to know that a solution will likely arrive eventually :)

Unfortunately, the project I'm working on cannot use React's canary version, but I appreciate the suggestion nonetheless!

@MoSattler
Copy link

This is still ongoing - why was this closed? Is there a known work around?

@kiliman
Copy link
Collaborator

kiliman commented Jun 6, 2024

The current workaround is to update to the latest React v19. It is currently in RC and should be final soon.

The issue with defer and hydration is that when there are hydration errors, React will re-render the page client-side. This removes the code that Remix uses to process the deferred promise.

@MoSattler
Copy link

Interesting - thank you for the explanation @kiliman !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: Closed
Development

No branches or pull requests

10 participants