I have read some article about state change listener, As I am a very beginner to the svelte environment I can't figure out what is the most efficient way to listen to the state change.
Let us take state variable as X
and Y
$: if (X||Y) {
console.log("yes");
}
Use a combination of afterUpdate
and onDestroy
REPL: https://svelte.dev/repl/300c16ee38af49e98261eef02a9b04a8?version=3.38.2
import { afterUpdate, onDestroy } from 'svelte';
export function useEffect(cb, deps) {
let cleanup;
function apply() {
if (cleanup) cleanup();
cleanup = cb();
}
if (deps) {
let values = [];
afterUpdate(() => {
const new_values = deps();
if (new_values.some((value, i) => value !== values[i])) {
apply();
values = new_values;
}
});
} else {
// no deps = always run
afterUpdate(apply);
}
onDestroy(() => {
if (cleanup) cleanup();
});
}
Use writable
and subscribe
<script>
import { writable } from 'svelte/store';
const X = writable(0);
const Y = writable(0);
X.subscribe(value => {
console.log("X was changed", value);
});
Y.subscribe(value => {
console.log("Y was changed", value);
});
</script>
<button on:click={(e)=>{
X.update((val)=>val++)
}}>Change X</button>
<button on:click={(e)=>{
Y.update((val)=>val++)
}}>Change Y</button>
Svelte does not have an equivalent of useEffect. Instead, Svelte has reactive statements.
// Svelte
// doubled will always be twice of single. If single updates, doubled will run again.
$: doubled = single * 2
// equivalent to this React
let single = 0
const [doubled, setDoubled] = useState(single * 2)
useEffect(() => {
setDoubled(single * 2)
}, [single])
This may seem like magic, since you don't define dependencies or teardown functions, but Svelte takes care of all that under the hood. Svelte is smart enough to figure out the dependencies and only run each reactive statement as needed.
So if you want to run a callback every time a value updates, you can simply do this.
<script>
let value = ''
$: console.log(value)
</script>
<input type='text' name='name' bind:value />
In short, this will console log the value of the input every time the input's value changes.
That's about as concise as you can go, and is my go-to means of listening to state changes unless I need something more complex.
$: if(x || y) console.log('yes')
Though note that there may be a subtle bug here. If x
or y
were both truthy then turn falsy (e.g., they both became empty strings), this statement will not run.
Here, you basically recreated React's useEffect
. This works, but you can simplify the implementation in your REPL a lot using reactive statements.
<script>
let count = 1;
$: console.log(count)
</script>
<input type="number" bind:value={count}>
Stores are great for passing data between components without using props or context. Check out this answer for more: https://stackoverflow.com/a/67681054/11506995
In this case, though, we can simplify everything using regular reactive context (again).
<script>
let x = 0
let y = 0
$: console.log('x was changed', x)
$: console.log('y was changed', y)
</script>
<button on:click={() => x++}>Change x</button>
<button on:click={() => y++}>Change x</button>
I also have a detailed answer of comparing React's context
/useEffect
with Svelte's context
/stores
/reactive statements in Understanding Context in Svelte (convert from React Context)