diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index e8213d3cf4a8..77140dc6903d 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -36,12 +36,7 @@ let todos = $state([ ...modifying an individual todo's property will trigger updates to anything in your UI that depends on that specific property: ```js -// @filename: ambient.d.ts -declare global { - const todos: Array<{ done: boolean, text: string }> -} - -// @filename: index.js +let todos = [{ done: false, text: 'add more todos' }]; // ---cut--- todos[0].done = !todos[0].done; ``` @@ -64,6 +59,17 @@ todos.push({ > [!NOTE] When you update properties of proxies, the original object is _not_ mutated. +Note that if you destructure a reactive value, the references are not reactive — as in normal JavaScript, they are evaluated at the point of destructuring: + +```js +let todos = [{ done: false, text: 'add more todos' }]; +// ---cut--- +let { done, text } = todos[0]; + +// this will not affect the value of `done` +todos[0].done = !todos[0].done; +``` + ### Classes You can also use `$state` in class fields (whether public or private): @@ -85,7 +91,42 @@ class Todo { } ``` -> [!NOTE] The compiler transforms `done` and `text` into `get`/`set` methods on the class prototype referencing private fields. +> [!NOTE] The compiler transforms `done` and `text` into `get`/`set` methods on the class prototype referencing private fields. This means the properties are not enumerable. + +When calling methods in JavaScript, the value of [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) matters. This won't work, because `this` inside the `reset` method will be the ` +``` + +You can either use an inline function... + +```svelte + +``` + +...or use an arrow function in the class definition: + +```js +// @errors: 7006 2554 +class Todo { + done = $state(false); + text = $state(); + + constructor(text) { + this.text = text; + } + + +++reset = () => {+++ + this.text = ''; + this.done = false; + } +} +``` ## `$state.raw` @@ -127,3 +168,90 @@ 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`. + +## 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: + +```js +/// file: index.js +// @filename: index.js +// ---cut--- +/** + * @param {number} a + * @param {number} b + */ +function add(a, b) { + return a + b; +} + +let a = 1; +let b = 2; +let total = add(a, b); +console.log(total); // 3 + +a = 3; +b = 4; +console.log(total); // still 3! +``` + +If `add` wanted to have access to the _current_ values of `a` and `b`, and to return the current `total` value, you would need to use functions instead: + +```js +/// file: index.js +// @filename: index.js +// ---cut--- +/** + * @param {() => number} getA + * @param {() => number} getB + */ +function add(+++getA, getB+++) { + return +++() => getA() + getB()+++; +} + +let a = 1; +let b = 2; +let total = add+++(() => a, () => b)+++; +console.log(+++total()+++); // 3 + +a = 3; +b = 4; +console.log(+++total()+++); // 7 +``` + +State in Svelte is no different — when you reference something declared with the `$state` rune... + +```js +let a = +++$state(1)+++; +let b = +++$state(2)+++; +``` + +...you're accessing its _current value_. + +Note that 'functions' is broad — it encompasses properties of proxies and [`get`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)/[`set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) properties... + +```js +/// file: index.js +// @filename: index.js +// ---cut--- +/** + * @param {{ a: number, b: number }} input + */ +function add(input) { + return { + get value() { + return input.a + input.b; + } + }; +} + +let input = $state({ a: 1, b: 2 }); +let total = add(input); +console.log(total.value); // 3 + +input.a = 3; +input.b = 4; +console.log(total.value); // 7 +``` + +...though if you find yourself writing code like that, consider using [classes](#Classes) instead. diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index 7dd03a6b04d7..fe3cf727e285 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -53,6 +53,22 @@ In the case of a numeric input (`type="number"` or `type="range"`), the value wi If the input is empty or invalid (in the case of `type="number"`), the value is `undefined`. +Since 5.6.0, if an `` has a `defaultValue` and is part of a form, it will revert to that value instead of the empty string when the form is reset. Note that for the initial render the value of the binding takes precedence unless it is `null` or `undefined`. + +```svelte + + +
+ + +
+``` + +> [!NOTE] +> Use reset buttons sparingly, and ensure that users won't accidentally click them while trying to submit the form. + ## `` Checkbox and radio inputs can be bound with `bind:checked`: @@ -64,16 +80,29 @@ Checkbox and radio inputs can be bound with `bind:checked`: ``` +Since 5.6.0, if an `` has a `defaultChecked` attribute and is part of a form, it will revert to that value instead of `false` when the form is reset. Note that for the initial render the value of the binding takes precedence unless it is `null` or `undefined`. + +```svelte + + +
+ + +
+``` + ## `` Inputs that work together can use `bind:group`. ```svelte @@ -146,6 +175,16 @@ When the value of an ` + + + +``` + ## `