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

fix: skip hydration validation when static parts have static classes #4278

Merged
merged 5 commits into from
Jun 11, 2024

Conversation

jmsjtu
Copy link
Member

@jmsjtu jmsjtu commented Jun 10, 2024

Details

Fixes #4270

Does this pull request introduce a breaking change?

  • 😮‍💨 No, it does not introduce a breaking change.

Does this pull request introduce an observable change?

  • 🔬 Yes, it does include an observable change.

Hydration validation will no longer fail incorrectly for statically optimized elements.

GUS work item

W-15963098

@jmsjtu jmsjtu requested a review from a team as a code owner June 10, 2024 21:16
Comment on lines +833 to +835
const hasMatchingStyleAttr = shouldValidateAttr(data, 'style')
? validateStyleAttr(vnode, elm, data, renderer)
: true;
Copy link
Member Author

Choose a reason for hiding this comment

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

validateStyleAttr already checks if the style attribute exists in the static parts but I decided to check here as well to make it clear why we can skip validating it for statically optimized elements.

Comment on lines +15 to +19
expect(p).toBe(snapshots.p);
expect(p.className).toBe(snapshots.classes);
// static classes are skipped by hydration validation
expect(consoleCalls.error).toHaveSize(0);
},
Copy link
Member Author

Choose a reason for hiding this comment

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

This test fails without the fix

Copy link
Collaborator

@nolanlawson nolanlawson left a comment

Choose a reason for hiding this comment

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

I'm concerned we are squelching true mismatch warnings, see comment above.

packages/@lwc/engine-core/src/framework/hydration.ts Outdated Show resolved Hide resolved
@@ -0,0 +1,4 @@
<template>
<!-- presence of a dynamic attribute creates a static part -->
<p class="c1" style={s1}>text</p>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we also test the reverse case, where the style is static but the class is dynamic?

: true;
const hasMatchingClass = shouldValidateAttr(data, 'className')
? validateClassAttr(vnode, elm, data, renderer)
: true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not so sure about this logic. Consider this case: 8a22043.

If you have a static element:

<p class="c1" lwc:ref="p">text</p>

but then you mutate it in renderedCallback:

renderedCallback() {
    this.refs.p.classList.add('hahaha!');
}

then we would expect a hydration mismatch error, because the client side is adding the new class (via renderedCallback), and renderedCallback is not invoked server-side.

This works correctly in master (hydration mismatch warning). However, with this PR, the hydration mismatch warning actually gets squelched.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point!

I hadn't considered that scenario, I'll add a test case for it and look into how to address it.

We'll need to have a way to determine what the static class/style values are during hydration.

Copy link
Member Author

@jmsjtu jmsjtu Jun 11, 2024

Choose a reason for hiding this comment

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

I think there might be an additional scenario we're skipping here.

If the renderedCallback dynamically adds a class to an element without static parts, we won't be able to validate against it either:

<p class="c1">text</p>
renderedCallback() {
    this.template.querySelector('p').classList.add('hahaha!');
}

Since the <p> doesn't have any static parts we're bypassing the hydration validation.

Here's a demo 628e711 from master.

The classes don't match but there's no hydration validation error.

The same might apply for style as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

During this function, we are comparing two things:

  1. the element created as a result of SSR HTML
  2. the VDOM generated client-side

renderedCallback is not involved in creating thing 1. It is also not involved in creating thing 2; renderedCallback may mutate the DOM directly, but it has no mechanism to mutate the VDOM. Additionally, at the point in time when this code is running, renderedCallback has not executed yet. It operates after VDOM is generated & the diffing algo updates corresponding elements.

In other words, this scenario should not be a concern & I believe the logic in the PR should be fine.

Copy link
Contributor

Choose a reason for hiding this comment

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

That said, I don't know why it results in a hydration mismatch in main... that's a little concerning. Let me take a quick look at that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at 8a22043, I don't know why that would fail in main. I'm kind of thinking that may have been a failure case that shouldn't have been a failure case, i.e. it should pass hydration without issue.

Copy link
Collaborator

@nolanlawson nolanlawson Jun 11, 2024

Choose a reason for hiding this comment

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

I was wondering if this was something that changed due to native custom element lifecycle, but I just tested with DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE = true and I'm still getting the same result (hydration mismatch) from 8a22043.

I think you might be right @divmain – my test case is a little busted because the ordering goes like this:

  • hydrateComponent()
  • renderedCallback()
  • test() <-- checks that the p.className is what we expect

I guess if we only care about validating SSR'd HTML against the VDOM at the moment before connectedCallback/renderedCallback, then yes the current code should be fine.

Copy link
Collaborator

@nolanlawson nolanlawson Jun 11, 2024

Choose a reason for hiding this comment

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

Looking at 8a22043, I don't know why that would fail in main.

It fails because it needs the fix from this PR. 🙂 If you apply this PR on top of that commit, then the only error is from the test() function itself (because it runs after renderedCallback and checks that the p.className is what it expects). There are no hydration mismatches.

Co-authored-by: Nolan Lawson <nlawson@salesforce.com>
Copy link
Contributor

@divmain divmain left a comment

Choose a reason for hiding this comment

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

Once you have the test for the case "where the style is static but the class is dynamic", I think this PR looks good to me.

: true;
const hasMatchingClass = shouldValidateAttr(data, 'className')
? validateClassAttr(vnode, elm, data, renderer)
: true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at 8a22043, I don't know why that would fail in main. I'm kind of thinking that may have been a failure case that shouldn't have been a failure case, i.e. it should pass hydration without issue.

@jmsjtu jmsjtu enabled auto-merge (squash) June 11, 2024 17:43
@jmsjtu jmsjtu merged commit 345c68d into master Jun 11, 2024
10 checks passed
@jmsjtu jmsjtu deleted the jtu/static-parts-hydration-validation-fix branch June 11, 2024 18:09
@jmsjtu jmsjtu mentioned this pull request Jun 11, 2024
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

Successfully merging this pull request may close these issues.

Hydration validation incorrectly fails for statically optimized content
3 participants