fix: loosen proxy signal creation heuristics (#11109)

* fix: loosen proxy signal creation heuristics

* tighten up test

* update comment

* no need to create a source outside an effect here, because it can't result in a reference

* preserve reference to props.$$events

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/11111/head
Dominic Gannaway 9 months ago committed by GitHub
parent 27891cb2dd
commit 3bb231197e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: loosen proxy signal creation heuristics

@ -19,7 +19,6 @@ import {
import { add_owner, check_ownership, strip_owner } from './dev/ownership.js';
import { mutable_source, source, set } from './reactivity/sources.js';
import { STATE_SYMBOL } from './constants.js';
import { updating_derived } from './reactivity/deriveds.js';
import { UNINITIALIZED } from '../../constants.js';
/**
@ -207,13 +206,8 @@ const state_proxy_handler = {
const metadata = target[STATE_SYMBOL];
let s = metadata.s.get(prop);
// if we're reading a property in a reactive context, create a source,
// but only if it's an own property and not a prototype property
if (
s === undefined &&
(current_effect !== null || updating_derived) &&
(!(prop in target) || get_descriptor(target, prop)?.writable)
) {
// create a source, but only if it's an own property and not a prototype property
if (s === undefined && (!(prop in target) || get_descriptor(target, prop)?.writable)) {
s = (metadata.i ? source : mutable_source)(proxy(target[prop], metadata.i, metadata.o));
metadata.s.set(prop, s);
}
@ -281,7 +275,7 @@ const state_proxy_handler = {
// we do so otherwise if we read it later, then the write won't be tracked and
// the heuristics of effects will be different vs if we had read the proxied
// object property before writing to that property.
if (s === undefined && current_effect !== null) {
if (s === undefined) {
// the read creates a signal
untrack(() => receiver[prop]);
s = metadata.s.get(prop);

@ -53,7 +53,7 @@ export function asClassComponent(component) {
class Svelte4Component {
/** @type {any} */
#events = {};
#events;
/** @type {Record<string, any>} */
#instance;
@ -70,7 +70,7 @@ class Svelte4Component {
// Using proxy state here isn't completely mirroring the Svelte 4 behavior, because mutations to a property
// cause fine-grained updates to only the places where that property is used, and not the entire property.
// Reactive statements and actions (the things where this matters) are handling this properly regardless, so it should be fine in practise.
const props = proxy({ ...(options.props || {}), $$events: this.#events }, false);
const props = proxy({ ...(options.props || {}), $$events: {} }, false);
this.#instance = (options.hydrate ? hydrate : mount)(options.component, {
target: options.target,
props,
@ -79,6 +79,8 @@ class Svelte4Component {
recover: options.recover
});
this.#events = props.$$events;
for (const key of Object.keys(this.#instance)) {
if (key === '$set' || key === '$destroy' || key === '$on') continue;
define_property(this, key, {

@ -1,5 +1,5 @@
<script>
const { settings } = $props();
import { settings } from './main.svelte';
</script>
Child: {settings.showInRgb}

@ -1,19 +1,19 @@
<script context="module">
export const context = $state({
const context = $state({
settings: {
showInRgb: true
}
})
});
export const settings = context.settings;
</script>
<script>
import Child from './Child.svelte';
const { settings } = context
</script>
<button onclick={() => settings.showInRgb = !settings.showInRgb}>
click {settings.showInRgb}
</button>
<Child settings={settings} />
<Child />

Loading…
Cancel
Save