How to synchronized stores #11449
-
A recent requirement had me synchronize certain properties in a settings (localstorage backed) store with certain properties in superforms form store.
The settings are not always available, therefore No easy solution, bummer.
Let's investigate. The The trace of the order of operation looks like so:
Instead of this:
The solution is to unsubscribe the other store subscriber, before import type { Unsubscriber, Writable } from "svelte/store";
function noop() { }
class State {
unsubscribeOne = noop
unsubscribeTwo = noop
unsubscribe() {
this.unsubscribeOne()
this.unsubscribeTwo()
this.unsubscribeOne = noop
this.unsubscribeTwo = noop
}
}
export function synchronize<T>(one: Writable<T>, two: Writable<T>, get: (item: T) => { [K in keyof T]?: T[K] }): Unsubscriber {
let state = new State();
createSubscribeOne()
createSubscribeTwo()
function createSubscribeOne() {
state.unsubscribeOne = subscribePreventReenty(one, two, 'one', () => {
state.unsubscribeTwo()
return () => {
createSubscribeTwo()
}
});
}
function createSubscribeTwo() {
state.unsubscribeTwo = subscribePreventReenty(two, one, 'two', () => {
state.unsubscribeOne()
return () => {
createSubscribeOne()
}
});
}
function subscribePreventReenty(sender: Writable<T>, receiver: Writable<T>, name: string, resubscribeReceiver: () => (() => void)) {
let initial = true
return sender.subscribe(sender => {
if (initial) {
console.log(`${name}: initial`)
initial = false
return
}
console.log(`${name}: begin`)
var subscribe = resubscribeReceiver()
receiver.update(receiver => {
console.log(`${name}: update other`)
return { ...receiver, ...get(sender) }
})
console.log(`${name}: end`)
subscribe()
})
}
return state.unsubscribe
} Is there a feature to synchronize stores, without this hack-job of a solution? Does anyone have a better idea on how to solve this? onMount(
synchronize(loadoutForm, localSettings, (o) => {
return { league: o.league, ... };
})
) Now that I think about it a parameter in the type PostUpdateState = "default" | "unchanged" | "modified";
export type Updater<T> = (value: T) => T | (value: T) => (T, PostUpdateState); Thanks a lot in advance. Merry Christmas everyone! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
I'm not sure if this is what you're after, but I've encountered similar requirements as you describe before. In my scenario, the changes were made by a user to a form. Instead of using eg: <script>
const store1 = writable('') // build from local storage
const store2 = writable('') // build from superforms
const store3 = derived([store1, store2], ([$store1, $store2]) => {
// choose value from either store1 or 2
return $store1 ?? $store2
})
</script>
synchronized input 1:
<input type='text' on:input={(e) => set(store1, e.target.value)} value={$store3} />
synchronized input 2:
<input type='text' on:input={(e) => set(store2, e.target.value)} value={$store3} /> |
Beta Was this translation helpful? Give feedback.
I'm not sure if this is what you're after, but I've encountered similar requirements as you describe before. In my scenario, the changes were made by a user to a form. Instead of using
bind:value={value}
on the input, I added aon:input
handler that would update the store and then set the input's value explicitly using a derived store.eg: