A Svelte Version of useEffect
How to port useEffect like code to Svelte using stores and reactive statements.
A challenge I came across porting some React code to Svelte is that I could not find a Svelte equivalent of
Svelte has support for turning a listener based API into a custom store that can also be reactive.
This replaces some usages of
However, for cases where we want to perform reactive effects that only trigger side effects I was unsure how to port the code to Svelte.
The React code to port:
You can see that when the track instance is swapped we remove the listener on the previous track, and add one on the new track.
The behavior we want is:
- We want to add a listener to the
- That listener will call a
stopAllTracks()method on the track (don't think too hard about it, it's just an example of some kind of side effect).
- When the component is unmounted we will remove the listener on the track.
- When the
trackprop changes (replaced with a new track) we remove the listener from the previous track and add one on the new track.
- This is very important, otherwise our component would be in an invalid state with respect to its props.
- Not handling the track prop changing could be considered a bug, and it could be difficult to diagnose.
- Hooks are intended to help prevent this sort of bug in React.
- You can read about this in Dan Abramov's article, React as a UI Runtime and in Writing Resilient Components.
This is what I've come up with for Svelte (please let me know if you know a better way):
The implementation of
useEffect is very simple:
useEffect function returns a custom store. Stores in Svelte should implement a
subscribe method is passed an
onChange function to alert Svelte about new data, and it can return a cleanup function.
Since we're only interested in side effects we don't use the
onChange function in our use case.
Svelte has support for creating stores reactively. Svelte also has support for auto subscribing to a store using
So we can use this custom store creating function like so:
effect is assigned using a reactive declaration, so a new store is created whenever
track is changed.
The Svelte compiler does the dependency tracking work for us, so there's no dependency array like in React.
Using another reactive statement with
$effect we have Svelte automatically handle subscribing and unsubscribing to the store, without this statement the store will not do anything.
With this we have replicated the most important behaviors of
This implementation isn't too bad compared to the React version. I'd still be interested in Svelte making things easier so I've opened a feature request for Reactive statement cleanup functions.
Tweet about this post and it will show here.