-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Svelte 5: the $sync rune #11380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
If you want to abstract the modification logic it is not necessary to synchronize two states, just use some kind of getter and setter |
Having two competitive sources of truth isn't a good design to begin with. But it can be unavoidable when using libs. function $sync(a, b) {
$effect(() => { a = b; });
$effect(() => { b = a; });
} |
@Thiagolino8 The REPL doesn't work as intended parent and child are not synced. When you click the checkbox it errors (see the console). @7nik well if The "Checker" example while simple contains the concepts and gotchas of a more general situation, which will be common in the Signals era: A few related elements, whose state+behavior are driven by a class encapsulating the logic (which can also come from a library). Now the class is instanciated from the component props, a few of which must be kept in sync since they are The conceptual problem here can be formulated as reactivity crossing the boundary of the class. There are several ways to go about this but we currently always have to pick among:
And on a stylistic point the synchronization intent is spread on several disconnected blocks. $effect(()=>{a = b});
$effect(()=>{b = a}); Now with
|
Typical
I strongly feel it will shoot in your leg when you try to sync nested props ( |
The fixed REPL is interesting as it works and doesn't have the mentionned shortcomings. But one could argue that:
For the logic Also notice that the |
I'm not sure, why this is needed. Here is an example, of multiple states: |
I'm probably missing something but what's wrong with this? |
It's supposed the class is third-party or a complex/shared logic you moved out to its own file. |
Hey there, I've been thinking (and tinkering) a lot about two-way bindings of things that require a transformation:
There is no obvious way to do this without event listeners, and having a mean to $sync states together would be nice. edit I managed to create a const cross = ([a, toB], [b, toA]) => $effect.root(() => {
let skipA = true
let skipB = true
$effect(() => {
a()
if (skipA) skipA = false
else {
toB()
skipB = true
}
})
$effect(() => {
b()
if (skipB) skipB = false
else {
toA()
skipA = true
}
})
}) Here are the previous examples with this
edit 2 Here is the generalized cross sync functions! const cross = (...pairs) => {
let skips = pairs.map(_ => true)
for (const [i, [dependency, action]] of pairs.entries()) {
$effect(() => {
dependency()
if (skips[i]) skips[i] = false
else {
action()
skips = skips.map((skip, j) => i === j ? skip : true)
}
})
}
} |
Yes something is missing as the parent state is not updating when just clicking on the checkbox :) |
Oh, I assumed that was explicitly what you were trying to prevent, so that the child state could temporarily diverge from the parent state. Otherwise why not just use a binding? |
The idea seems to be $bindable functionality but for class properties(and variables in general) instead of just component props. |
Yes in the case of a simple checkbox we would indeed just use a binding (this is where the example is too simple). But as we move towards using classes for more complex and refactorable internal state+behavior management then the situation is more complex than the props <-> element direct two-way binding. In general it looks like props <-> internal state <-> element. And this is where a So the challenge is to make the "Checker" work but keep the internal state in the class to have a taste of something more generalizable to complex situations. Here is a less contrived example from Discord (thanks @ottomated) where an element has an internal state and the value is bound. But the ideas to go about this always revolve around using multiple |
I want to bring out that For example: // inline
let {checked = $bindable(false).sync(checker.checked)} = $props()
// or
let {checked = $bindable(false)} = $props()
$bindable(checked).sync(checker.checked) Where the argument to bindable is required to be either a bindable rune or its default value during init. Also maybe |
Btw, are there any use cases where |
I think yes, since you could use it for any case where there's just a class with properties you want to sync to. You instantiate the class, and then sync some other variables all within a single component. |
Closing this as we've investigated it at length and repeatedly concluded that passing thunks and callbacks around is the right approach |
Uh oh!
There was an error while loading. Please reload this page.
Describe the problem
Feature request: a native Svelte way to keep two
$states
in sync.Motivation
This naturally arises in contexts where a value can be updated in multiple ways, eg. a
$bindable
prop synced with a class field.The logic is: if A is dirty, update B, if B is dirty update A.
At the moment to do this properly we would have to make a custom wrapper around
$state
ful values to track which one is dirty, when the signals already have this informationExample
And without a wrapper the design has several gottchas REPL:
$effect
fires too many timesDescribe the proposed solution
The
$sync
rune$sync(A,B)
does what it feels like: when A is dirty it updates B, when B is dirty it updates A.This way:
$derived
+$effect
or$effect
+$effect
blocks$effect
over-firing$derived
on every single prop changeCorollary:
With this rune the example can be written:
Importance
would make my life easier
The text was updated successfully, but these errors were encountered: