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

Client render Suspense content if there's no boundary match #16945

Merged
merged 1 commit into from Oct 16, 2019

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Sep 28, 2019

Without the enableSuspenseServerRenderer flag there will never be a boundary match. Also when it is enabled, there might not be a boundary match if something was conditionally rendered by mistake.

With this PR it will now client render the content of a Suspense boundary in that case and issue a DEV only hydration warning. This is the only sound semantics for this case.

Unfortunately, landing this will once again break #16938. It will be less bad though because at least it'll just work by client rendering the content instead of hydrating and issue a DEV only warning.

However, we must land this before enabling the enableSuspenseServerRenderer flag since it does this anyway.

I did notice that we special case fallback={undefined} due to our unfortunate semantics for that. So technically a workaround that works is actually setting the fallback to undefined on the server and during hydration. Then flip it on only after hydration. That could be a workaround if you want to be able to have a Suspense boundary work only after hydration for some reason.

It's kind of unfortunate but at least those semantics are internally consistent. So I added a test for that.

cc @Timer @timneutkens

Without the enableSuspenseServerRenderer flag there will be no boundary
match. Also when it is enabled, it will client-render the Suspense content
and treat it as a mismatch if there's no boundary in the HTML.
@sizebot
Copy link

sizebot commented Sep 28, 2019

No significant bundle size changes to report.

Generated by 🚫 dangerJS against 1857006

element,
),
).toWarnDev(
'Warning: Did not expect server HTML to contain a <div> in <div>.',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warns because of the superfluous div but before that it should warn about missing a Suspense boundary but it's currently missing that warning:

https://github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOMHostConfig.js#L843

@Timer
Copy link

Timer commented Sep 28, 2019

FYI, Next.js' specific <Suspense> component did have a fallback defined. The reproduction I opened omitted it, sorry about that!

The behavior Next.js relied on, represented as a test:

  it('Suspense + hydration in legacy mode with fallback', () => {
    const element = document.createElement('div');
    element.innerHTML = '<div>Hello World</div>';
    let div = element.firstChild;
    let ref = React.createRef();

    ReactDOM.hydrate(
      <React.Suspense fallback={<div>Loading...</div>}>
        <div ref={ref}>Hello World</div>
      </React.Suspense>,
      element,
    );

    // Suspense component did not suspend during hydration, so
    // it was hydrated as if it was absent.
    expect(ref.current).toBe(div);
    expect(element.innerHTML).toBe('<div>Hello World</div>');
  });

☝️ this worked because Next.js guaranteed nothing in the tree would suspend during hydration / initial render.

@Timer
Copy link

Timer commented Sep 28, 2019

We released next@9.0.7 in response to react-dom@16.10.0, removing our top level Suspense wrapper: vercel/next.js#8887.

Meaning, next@9.0.7 is compatible with react-dom@16.10.0 and does not require react-dom@16.10.1.

We don't necessarily need this old behavior to be supported -- the Suspense boundary was not used for any stable Next.js feature (it was flagged behind an experimental feature).

However, this has been released in Next.js for ~6 months (April 2nd).
If necessary, we could cut a patch for our 8.1.x release line to remove the Suspense boundary.

Time (and cross-team coordination) could probably heal this, the 16.10.0 release was just painful because there was no 9.0.x version compatible (and still no 8.1.x version compatible).


End-users and other companies in the ecosystem may have been relying on this behavior though (see comment above). We've seen companies/apps rely on this behavior in the wild.

Areas (apps built with or inspired from) we should probably look into:

@acdlite
Copy link
Collaborator

acdlite commented Sep 28, 2019

@Timer

Time (and cross-team coordination) could probably heal this

Yeah we realized when discussing this incident that we really should be running smoke tests on the major React frameworks as part of our release plan.

However, this has been released in Next.js for ~6 months (April 2nd).
If necessary, we could cut a patch for our 8.1.x release line to remove the Suspense boundary.

That might be wise. We'll wait to land this change until after we figure out what to do in Next and the other projects that might be relying on this accidental behavior.

In the meantime, I marked react-dom@16.10.0 as deprecated so we should be fine for now.

@timneutkens
Copy link
Contributor

I'm not opposed to releasing a 8.1.1 that removes the suspense boundary too, even though Next.js 9 doesn't really break in most cases.

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

Successfully merging this pull request may close these issues.

None yet

7 participants