I am trying to port an Vue application to Svelte and got stuck on watched properties.
In Vue, we have ability to watch particular property and get handler called with new value and old value of this property.
In Svelte, I am not sure if I can somehow utilize $: syntax to watch single variable and execute code upon change. This code may reference other variables that should not be watched.
So my questions are:
Thanks!
This was something I was mulling over a bit too recently (I also have quite a few components I've written in Vue). I've found a lot of the time I was actually able to refactor a lot of what was setup with watchers to actually be reactive, so def don't assume you can't go that route.
But it's not always the case obviously. There is a relatively straight forward way to do it—the drawback is that you'll end up with an extra variable for every single variable you're needing to watch/use old value.
let i = 0, // value to "watch"
n = 0, // "old" value
o = 0; // any other value you don't want "watched"
$: if (n !== i) {
console.log(n, i, o);
n = i;
}
Any time you change i or o, your reactive statement will check to see if it was actually i that has changed (since it's old value is still assigned to n). If it was o that changed, nothing runs.
If there's a better way I haven't come across it yet, but other than the extra var it's a pretty simple solution.
<script>
let a = 1;
let b = 2;
function compareToB(x) {
console.log(x - b);
}
$: compareToB(a) // this line fires only when `a` changes, but the body of `compareToB` depends also on `b`
</script>
Edit: https://svelte.dev/repl/c80d6ccfa8b242a49def281b9f32027d?version=3.2.2
Yes, that's a good solution too—you're mostly just swapping an extra var for an extra function.
Thanks for the thumbs down bro. 👍🏼
Yes, swapping extra var for an extra function, increased clarity and generated code with possibly better performance, since the comparison and branch are entirely eliminated.
Edit: I guess I shouldn't have thumb-downed your solution, since it also works. Sorry, dude.
It did seem a bit harsh, but no worries. I get that there are probably some extra invalidation calls being made in my example, don't think it would have a particularly measurable impact on perf.
Keep in mind you still have to retain the old value of "a" to answer the OP.
<script>
let a = 1;
let b = 2;
let c = a;
$: compareToB(a)
function compareToB(x) {
console.log(x - b);
c = x;
}
</script>
Thank you both! Both of the responses were useful to understand this.
If we use @mrobakowski's solution, we don't need to store old value, if we don't use it.
However, I still think that cleaner API could be provided, or at least much more precise docs.
Could you _react_ to this @Rich-Harris? 😄
How to get previous value of changed variable?
The update function of a writable store gets you access to that stores previous value. So you could create a custom store that acts as a watcher and calls a watch function on updates. Bit complex initially but then you don't have to create and mange any extra variables.
Example:
/* ---- watcher.js ---- */
import { writable } from 'svelte/store'
function watcher (initialValue, watchFunction) {
const { subscribe, update } = writable(initialValue)
return {
subscribe,
set: value => {
update(oldvalue => {
watchFunction(oldvalue, value)
return value
})
}
}
}
export default watcher
/* ---- Component.svelte ---- */
md5-bd7205f2a9f011bc398f5c597e0f8bc0
md5-b724b748245cd2e0513e237fc160af99
<button on:click={inc}>INCREMEMNT</button>
REPL example:
https://svelte.dev/repl/1749a78efd5545d288154cbb43a52188?version=3.4.1
How can a watch component attribute? For example I have export let show = false in the component and I need to run some code when it changes to true or false, how can I do this, I include component following way: <SendForm show={showSendForm}/>.
$: console.log(watch);
Should log out the value of watch each time it changes.
@unlocomqx It's a dev tool, not relevant to the question
Most helpful comment
The
updatefunction of a writable store gets you access to that stores previous value. So you could create a custom store that acts as a watcher and calls a watch function on updates. Bit complex initially but then you don't have to create and mange any extra variables.Example:
REPL example:
https://svelte.dev/repl/1749a78efd5545d288154cbb43a52188?version=3.4.1