-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Describe the bug
This is hard for me to explain, but I will try my best. I first identified this issue from my SvelteKit app after migrating, but was able to replicate the bad behavior in standalone Svelte and even on the REPL.
When passing a prop to a child component, the value of the prop is seemingly recomputed or revalidated during dismount if both of the following are true:
- The value of the prop is consumed by the component in some way during
onDestroy - The value of the prop is NOT consumed by the component's template
To demonstrate this, I wrapped the value of the prop (from the parent component) in a middleman logger function, e.g.
<DataConsumer data={log(data)} />
Ideally, this log function would only run initially when the component mounts (and in other situations, when the data changes).
However, when the child component is dismounted, that log middleman function is invoked by Svelte an extra time when the above conditions are true. Otherwise, the log function is invoked as expected.
To clarify, a middleman function is not required, and is merely being used to show that the prop is being revalidated. Any complex logic in the prop can cause render-breaking errors.
In an environment like SvelteKit, the data is often undefined at this time (such as when it is derived from page data and we're navigating away), which often led to "Cannot read properties of undefined" errors. For example:
<LinkedData data={data.reviews.richData} />
Currently in SvelteKit, if the LinkedData component matches all of the above conditions, then this code would cause a render-breaking error when navigating away, as data becomes undefined before it attempts to revalidate the prop expression data.reviews.richData.
Reproduction
https://svelte.dev/playground/085d926d44e845e6b3d06cc59a6f9fc8?version=5.1.4
The "toggle consumer" button will mount/dismount the child component. Toggling it with initial state will yield correct behavior, in that the log function is only called once at mounting time:
Data computed for prop: { example: "This is some example data" }
DataConsumer mounted
DataConsumer destroyed
However, when only the "Reference data in onDestroy?" checkbox is enabled, the log function is invoked an extra during unmounting, which is clear from the duplicate log line.
Data computed for prop: { example: "This is some example data" }
DataConsumer mounted
Data computed for prop: { example: "This is some example data" }
DataConsumer destroyed
The extra invocation of the log function directly corresponds to where in the onDestroy function the property's value was used. For example, you can move it below the console.log and the above output would be rearranged.
If both checkboxes are enabled (i.e. the template somehow consumes the prop) then the behavior returns to normal.
Logs
No response
System Info
Google Chrome 130.0.6723.70
Svelte 5.1.4Severity
blocking an upgrade