@ -166,6 +166,54 @@ 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
<script>
import Counter from 'external-class';
let counter = $state(new Counter());
function increment() {
counter.increment(); // `counter`'s internal state has changed, but Svelte doesn't know that yet
$state.invalidate(counter);
}
</script>
<buttononclick={increment}>
Count is {counter.count}
</button>
```
`$state.invalidate` can also be used with reactive class fields, and properties of `$state` objects:
```js
class Box {
value;
constructor(initial) {
this.value = initial;
}
}
class Counter {
count = $state(new Box(0));
increment() {
this.count.value += 1;
$state.invalidate(this.count);
}
}
let counter = $state({count: new Box(0)});
function increment() {
counter.count.value += 1;
$state.invalidate(counter.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:
@ -196,6 +196,12 @@ This restriction only applies when using the `experimental.async` option, which
Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.
```
### state_invalidate_invalid_source
```
The argument passed to `$state.invalidate` must be a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
@ -953,6 +953,37 @@ Cannot export state from a module if it is reassigned. Either export a function
`%rune%(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.
```
### state_invalidate_invalid_this_property
```
`$state.invalidate` can only be called with an argument referencing `this` in a class using a non-computed property
```
Like how you can't use `$state` or `$derived` when declaring computed class fields, you can't use `$state.invalidate` to invalidate a computed class field. For example, while `count` here is not itself a computed property, you can't invalidate it if you reference it in a computed property:
```js
class Box {
value;
constructor(initial) {
this.value = initial;
}
}
const property = 'count';
class Counter {
count = $state(new Box(0));
increment() {
this.count.value += 1;
$state.invalidate(this[property]); // this doesn't work
$state.invalidate(this.count); // this works
}
}
```
### state_invalidate_nonreactive_argument
```
`$state.invalidate` only takes a variable or non-computed class field declared with `$state` or `$state.raw` as its argument
@ -146,6 +146,10 @@ This restriction only applies when using the `experimental.async` option, which
> Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.
## state_invalidate_invalid_source
> The argument passed to `$state.invalidate` must be a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
> `%rune%(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.
## state_invalidate_invalid_this_property
> `$state.invalidate` can only be called with an argument referencing `this` in a class using a non-computed property
Like how you can't use `$state` or `$derived` when declaring computed class fields, you can't use `$state.invalidate` to invalidate a computed class field. For example, while `count` here is not itself a computed property, you can't invalidate it if you reference it in a computed property:
```js
class Box {
value;
constructor(initial) {
this.value = initial;
}
}
const property = 'count';
class Counter {
count = $state(new Box(0));
increment() {
this.count.value += 1;
$state.invalidate(this[property]); // this doesn't work
$state.invalidate(this.count); // this works
}
}
```
## state_invalidate_nonreactive_argument
> `$state.invalidate` only takes a variable or non-computed class field declared with `$state` or `$state.raw` as its argument
## store_invalid_scoped_subscription
> Cannot subscribe to stores that are not declared at the top level of the component
@ -529,6 +529,24 @@ export function state_invalid_placement(node, rune) {
e(node,'state_invalid_placement',`\`${rune}(...)\` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.\nhttps://svelte.dev/e/state_invalid_placement`);
e(node,'state_invalidate_invalid_this_property',`\`$state.invalidate\` can only be called with an argument referencing \`this\` in a class using a non-computed property\nhttps://svelte.dev/e/state_invalidate_invalid_this_property`);
e(node,'state_invalidate_nonreactive_argument',`\`$state.invalidate\` only takes a variable or non-computed class field declared with \`$state\` or \`$state.raw\` as its argument\nhttps://svelte.dev/e/state_invalidate_nonreactive_argument`);
consterror=newError(`state_invalidate_invalid_source\nThe argument passed to \`$state.invalidate\` must be a variable or class field declared with \`$state\` or \`$state.raw\`, or a property of a \`$state\` object.\nhttps://svelte.dev/e/state_invalidate_invalid_source`);