Skip to content

Conversation

jmsjtu
Copy link
Member

@jmsjtu jmsjtu commented Sep 6, 2025

Details

Does this pull request introduce a breaking change?

  • 💔 Yes, it does introduce a breaking change.

This PR fixes an issue where disconnected nodes could be scheduled for rehydration in the framework's reactivity system.

This means that disconnected nodes could have lifecycle methods, getters/setters, and function invocations happen on nodes that were disconnected from the DOM.

Any downstream users that had a dependency on this behavior will be impacted.

Does this pull request introduce an observable change?

  • 🔬 Yes, it does include an observable change.

See ☝️

GUS work item

W-19482166

@jmsjtu jmsjtu requested a review from a team as a code owner September 6, 2025 03:09
Comment on lines +676 to +683
// We want to prevent rehydration from occurring when nodes are detached from the DOM as this can trigger
// unintended side effects, like lifecycle methods being called multiple times.
// For backwards compatibility, we use a flag to control the check.
// 1. When flag is disabled always rehydrate (legacy behavior)
// 2. When flag is enabled only rehydrate when the VM state is connected (fixed behavior)
if (!lwcRuntimeFlags.DISABLE_DETACHED_REHYDRATION || vm.state === VMState.connected) {
rehydrate(vm);
}
Copy link
Member Author

@jmsjtu jmsjtu Sep 6, 2025

Choose a reason for hiding this comment

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

This is the entire change, we just need to check that the vm is connected before rehydrating.

Times when the vm is disconnected before hydration occur during async race conditions.

Example scenario:

VM2 is scheduled for rehydration due to update from wire adapter.
VM1, grand-owner of VM2, is scheduled for rehydration.
VM1 rehydration goes first due to idx-sorting.
VM1 rendering disconnects VM2 from the DOM.
VM2 disconnectedCallback fires.
VM2 rehydration triggers, calling connectedCallback and render for entire disconnected subtree.

This will become a more apparent issue with the introduction of state managers, as the state manger can trigger reactivity asynchronously from outside of the component.

This issue has occurred frequently for team using redux.

Copy link
Member Author

Choose a reason for hiding this comment

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

@abdulsattar can you please verify if this will have an effect of HMR?

I see some usages of scheduleRehydration in the hot-swaps code.

Comment on lines +327 to +335
const expected = ['parent:connectedCallback'];

if (lwcRuntimeFlags.DISABLE_DETACHED_REHYDRATION) {
expected.push('parent:renderedCallback');
}

if (!lwcRuntimeFlags.DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE) {
expected.push('child:connectedCallback');
}
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 changed because previously, both the parent and child were being rehydrated while they were both disconnected from the DOM.

When the elements are re-inserted into the DOM, the VMs are not longer marked as dirty so rehydration is skipped.

The renderedCallback did not fire previously because the parent element was not connected to the DOM.

Comment on lines +61 to 64
- run: DISABLE_DETACHED_REHYDRATION=1 yarn sauce:ci
- run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_SYNTHETIC=1 yarn sauce:ci
- run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn sauce:ci

Copy link
Member Author

Choose a reason for hiding this comment

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

Adding test configs for when we can re-enable integration tests in CI.

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.

1 participant