Reputation: 7699
Given the case that I have two stores and a function, that should run every time one of the stores changes
export const s1 = writable('store1')
export const s2 = writable('store2')
export function onStoreChange(triggeredFrom) {
console.log('a store has changed! - ', triggeredFrom)
}
Ways I know to handle this 'double subscription' would be inside a .svelte file
$: $s1, $s2, onStoreChange('component')
and inside a javascript file with subscribing to the two stores seperately
const unsubS1 = s1.subscribe(v => {
onStoreChange('s1 subscription')
})
const unsubS2 = s2.subscribe(v => {
onStoreChange('s2 subscription')
})
but I'm wondering if there's a more concise way to this, combining the two (or more) subscriptions inside a .js file?
I thought about (mis)using a derived store
export const derivedStore = derived(
[s1, s2],
([$s1, $s2]) => {
onStoreChange('derived Store', $s1, $s2)
}
)
but in this case the store value isn't used anywhere and because of that it looks like the derived store doesn't 'survive' compiling - even if the value must have changed, the function inside doesn't run. Thanks to @voscausa for pointing out, that "a store will only run if it has a subscriber" - so the functionality of a derived store doesn't match directly to what I'm looking for but could be complemented with a subscription like this
export const derivedStore = derived(
[s1, s2],
([$s1, $s2]) => [$s1, $s2]
)
const unsubDerivedStore = derivedStore.subscribe(value => {
onStoreChange('derived Store', ...value)
)
Is this the best way for such a 'multi subscription' or is there an alternative?
a REPL
(Background behind the question is not only to maybe write less code, but to have both values 'directly available' without using get()
)
$: $s1, $s2, onStoreChange($s1, $s2)
// vs
const unsubS1 = s1.subscribe(s1 => {
onStoreChange(s1, get(s2))
})
const unsubS2 = s2.subscribe(s2Value => {
onStoreChange(get(s1), s2)
})
Upvotes: 1
Views: 3522
Reputation: 1176
If you want to subscribe to multiple stores without using derived
and want to use it in a JS file you can use something like this :
function subscribeMultiples(stores, callback)
{
// Store values of all the stores
const values = [];
const unsubscribes = [];
// Subscribe to all the stores
for (let i = 0; i < stores.length; i++)
{
unsubscribes[i] = stores[i].subscribe((value) => {
values[i] = value;
// Call the callback when all the stores have values
if (values.length == stores.length) callback(values);
});
}
return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe());
}
}
Which you can use like so :
subscribeMultiples([s1, s2], ([$s1, $s2]) => {
console.log("subscribeMultiples", $s1, $s2);
});
It's a simplified version of how derived
works.
Upvotes: 1
Reputation: 11706
I tested my code and it works fine.
s3 has to comeup with a new value every time s1 or s2 changes!
A store will do nothing if it's returned value does not change.
The array returned by s3 makes sure s3 will change every time s1 or s2 changes. But there is no need to use the array result in App.svelte. It's not used.
So here is my App.svelte:
<script>
import {s1, s2, s3} from "./stores.js";
let i = 0;
function fn() {
console.log("s3 triggered fn");
}
$: if ($s3) fn();
</script>
<h1>Test derived</h1>
<button on:click={() => {
i += 1;
$s1 = i;
}}>
button-s1
</button>
<button on:click={() => {
i += 1;
$s2 = i;
}}>
button-s2
</button>
And the stores.js
import {writable, derived} from "svelte/store"
export const s1 = writable(0);
export const s2 = writable(0);
export const s3 = derived([s1, s2], ([$s1, $s2]) => {
console.log("derived", $s1, $s2);
return [$s1, $s2];
});
Upvotes: 0
Reputation: 11706
This is where you can use a derived store like
const s3 = derived([s1, s2], ([$s1, $s2]) => [$s1, $s2]);
Store s3 returns an array with the store latest store values from both if one of these stores changes.
And you can create a derived store in a js file. The $ prefix here is to make clear it's the store value and not the store.
So this will work as well:
const s3 = derived([s1, s2], ([v1, v2]) => [v1, v2]);
Upvotes: 4