docs: tweak effect docs (#11982)

better examples 

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/12180/head
Rich Harris 2 weeks ago committed by GitHub
parent ff946812b7
commit 4540ff3118
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -335,49 +335,100 @@ In general, `$effect` is best considered something of an escape hatch — useful
> For things that are more complicated than a simple expression like `count * 2`, you can also use [`$derived.by`](#$derived-by). > For things that are more complicated than a simple expression like `count * 2`, you can also use [`$derived.by`](#$derived-by).
When reacting to a state change and writing to a different state as a result, think about if it's possible to use callback props instead. You might be tempted to do something convoluted with effects to link one value to another. The following example shows two inputs for "money spent" and "money left" that are connected to each other. If you update one, the other should update accordingly. Don't use effects for this ([demo](/#H4sIAAAAAAAACpVRy2rDMBD8lWXJwYE0dg-9KFYg31H3oNirIJBlYa1DjPG_F8l1XEop9LgzOzP7mFAbSwHF-4ROtYQCL97jAXn0sQh3skx4wNANfR2RMtS98XyuXMWWGLhjZUHCa1GcVix4cgwSdoEVU1bsn4wl_Y1I2kS6inekNdWcZXuQZ5giFDWpfwl5WYyT2fynbB1g1UWbTVbm2w6utOpKNq1TGucHhri6rLBX7kYVwtW4RtyVHUhOyXeGVj3klLxnyJP0i8lXNJUx6en-v6A48K85kTimpi0sYj-yAo-Wlh9FcL1LY4K3ahSgLT1OC3ZTXkBxfKN2uVC6T5LjAduuMdpQg4L7geaP-RNHPuClMQIAAA==)):
```svelte ```svelte
<!-- Don't do this -->
<script> <script>
let value = $state(); let total = 100;
let value_uppercase = $state(); let spent = $state(0);
let left = $state(total);
$effect(() => {
left = total - spent;
});
$effect(() => { $effect(() => {
value_uppercase = value.toUpperCase(); spent = total - left;
}); });
</script> </script>
<Text bind:value /> <label>
<input type="range" bind:value={spent} max={total} />
{spent}/{total} spent
</label>
<!-- Do this instead: --> <label>
<input type="range" bind:value={left} max={total} />
{left.value}/{total} left
</label>
```
Instead, use callbacks where possible ([demo](/#H4sIAAAAAAAACo2SP2-DMBDFv8rp1CFR84cOXQhU6p6tY-ngwoEsGWPhI0pk8d0rG5yglqGj37v7veMJh7VUZDH9dKhFS5jiuzG4Q74Z_7AXUky4Q9sNfemVzJa9NPxW6IIVMXDHQkEOL0lyipo1pBlyeLIsmDbJ9u4oqhdG2A2mLrgedMmy0zCYSjB9eMaGtuC8WXBkPtOBRd8QHy5CDXSa3Jk7HbOfDgjWuAo_U71kz9vr6Bgc2X44orPjow2dKfFNKhSTSW0GBl9iXmAvdEMFQqDmLgBH6HQYyt3ie0doxTV3IWqEY2DN88eohqePvsf9O9mf_if4HMSVXD89NfEI99qvbMs3RdPv4MXYaSWtUeKWQq3oOlfZCJNCcnildlFgWMcdtl0la0kVptwPNH6NP_uzV0acAgAA)):
```svelte
<script> <script>
let value = $state(); let total = 100;
let value_uppercase = $state(); let spent = $state(0);
function onValueChange(new_text) { let left = $state(total);
value = new_text;
value_uppercase = new_text.toUpperCase(); function updateSpent(e) {
spent = +e.target.value;
left = total - spent;
} }
</script>
<Text {value} {onValueChange}> function updateLeft(e) {
``` left = +e.target.value;
spent = total - left;
}
</script>
If you want to have something update from above but also modify it from below (i.e. you want some kind of "writable `$derived`"), and events aren't an option, you can also use an object with getters and setters. <label>
<input
type="range"
value={spent}
oninput={updateSpent}
max={total}
/>
{spent}/{total} spent
</label>
<label>
<input
type="range"
value={left}
oninput={updateLeft}
max={total}
/>
{left}/{total} left
</label>
```
If you need to use bindings, for whatever reason (for example when you want some kind of "writable `$derived`"), consider using getters and setters to synchronise state ([demo](/#H4sIAAAAAAAACo2SQW-DMAyF_4pl7dBqXcsOu1CYtHtvO44dsmKqSCFExFRFiP8-xRCGth52tJ_9PecpA1bakMf0Y0CrasIU35zDHXLvQuGvZJhwh77p2nPoZP7casevhS3YEAM3rAzk8Jwkx9jzjixDDg-eFdMm2S6KoWolyK6ItuCqs2fWjYXOlYrpPTA2tIUhiAVH5iPtWbUX4v1VmY6Okzpzp2OepgNEGu_CT1St2fP2fXQ0juwwHNHZ4ScNmxn1RUaCybR1HUMIMS-wVfZCBYJQ80GAIzRWhvJh9d4RanXLB7Ea4SCsef4Qu1IG68Xu387h9D_GJ2ne8ZXpxTZUv1w994amjxCaMc1Se2dUn0Jl6DaHeFEuhWT_QvUqOlnHHdZNqStNJabcdjR-jt8IbC-7lgIAAA==)):
```svelte ```svelte
<script> <script>
let { value } = $props(); let total = 100;
let facade = { let spent = $state(0);
let left = {
get value() { get value() {
return value.toUpperCase(); return total - spent;
}, },
set value(val) { set value(v) {
value = val.toLowerCase(); spent = total - v;
} }
}; };
</script> </script>
<input bind:value={facade.value} /> <label>
<input type="range" bind:value={spent} max={total} />
{spent}/{total} spent
</label>
<label>
<input type="range" bind:value={left.value} max={total} />
{left.value}/{total} left
</label>
``` ```
If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](functions#untrack). If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](functions#untrack).

Loading…
Cancel
Save