diff --git a/.changeset/unlucky-gorillas-hunt.md b/.changeset/unlucky-gorillas-hunt.md new file mode 100644 index 000000000000..055fc120a625 --- /dev/null +++ b/.changeset/unlucky-gorillas-hunt.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: widen ownership when calling setContext diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index e1088edf308d..bd94d5ad8a19 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -8,7 +8,8 @@ import { active_effect, active_reaction, set_active_effect, - set_active_reaction + set_active_reaction, + untrack } from './runtime.js'; import { effect } from './reactivity/effects.js'; import { legacy_mode_flag } from '../flags/index.js'; @@ -49,14 +50,6 @@ export function set_dev_current_component_function(fn) { export function getContext(key) { const context_map = get_or_init_context_map('getContext'); const result = /** @type {T} */ (context_map.get(key)); - - if (DEV) { - const fn = /** @type {ComponentContext} */ (component_context).function; - if (fn) { - add_owner(result, fn, true); - } - } - return result; } @@ -74,6 +67,15 @@ export function getContext(key) { */ export function setContext(key, context) { const context_map = get_or_init_context_map('setContext'); + + if (DEV) { + // When state is put into context, we treat as if it's global from now on. + // We do for performance reasons (it's for example very expensive to call + // getContext on a big object many times when part of a list component) + // and danger of false positives. + untrack(() => add_owner(context, null, true)); + } + context_map.set(key, context); return context; } @@ -100,16 +102,6 @@ export function hasContext(key) { */ export function getAllContexts() { const context_map = get_or_init_context_map('getAllContexts'); - - if (DEV) { - const fn = component_context?.function; - if (fn) { - for (const value of context_map.values()) { - add_owner(value, fn, true); - } - } - } - return /** @type {T} */ (context_map); } diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 70cfbb47f3b6..a9506cfdc079 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -109,7 +109,7 @@ export function mark_module_end(component) { /** * @param {any} object - * @param {any} owner + * @param {any | null} owner * @param {boolean} [global] * @param {boolean} [skip_warning] */ @@ -120,7 +120,7 @@ export function add_owner(object, owner, global = false, skip_warning = false) { if (metadata && !has_owner(metadata, component)) { let original = get_owner(metadata); - if (owner[FILENAME] !== component[FILENAME] && !skip_warning) { + if (owner && owner[FILENAME] !== component[FILENAME] && !skip_warning) { w.ownership_invalid_binding(component[FILENAME], owner[FILENAME], original[FILENAME]); } } @@ -165,7 +165,7 @@ export function widen_ownership(from, to) { /** * @param {any} object - * @param {Function} owner + * @param {Function | null} owner If `null`, then the object is globally owned and will not be checked * @param {Set} seen */ function add_owner_to_object(object, owner, seen) { @@ -174,7 +174,11 @@ function add_owner_to_object(object, owner, seen) { if (metadata) { // this is a state proxy, add owner directly, if not globally shared if ('owners' in metadata && metadata.owners != null) { - metadata.owners.add(owner); + if (owner) { + metadata.owners.add(owner); + } else { + metadata.owners = null; + } } } else if (object && typeof object === 'object') { if (seen.has(object)) return;