diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 49e17cd08f..e05590ce61 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -164,6 +164,47 @@ To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snaps This is handy when you want to pass some state to an external library or API that doesn't expect a proxy, such as `structuredClone`. +## `$state.invalidate` + +In the case that you aren't using a proxied `$state` via use of `$state.raw` or a class instance, you may need to tell Svelte a `$state` has changed. You can do so via `$state.invalidate`: + +```svelte + + +``` + +`$state.invalidate` can also be used with reactive class fields: + +```js +class Box { + value; + + constructor(initial) { + this.value = initial; + } +} + +class Counter { + count = $state(new Box(0)); + + increment() { + this.count.value++; + $state.invalidate(this.count); + } +} +``` + ## Passing state into functions JavaScript is a _pass-by-value_ language — when you call a function, the arguments are the _values_ rather than the _variables_. In other words: diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 36189cb749..3bb8a2e4cf 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -215,6 +215,15 @@ export function internal_set(source, value) { * @param {Source} source */ export function invalidate(source) { + if ( + active_reaction !== null && + !untracking && + is_runes() && + (active_reaction.f & (DERIVED | BLOCK_EFFECT)) !== 0 && + !reaction_sources?.includes(source) + ) { + e.state_unsafe_mutation(); + } source.wv = increment_write_version(); mark_reactions(source, DIRTY);