-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
add a way to force update a state variable #14520
Comments
Why does It sounds like you want a solution like this: #10560 (comment) |
Yes, there is some thing in the template that use it. Looking at the solution you provided, I do not want to wrap things on top of things when a simple force update function would do the job ( seems a lot cleaner and a lot less code ). |
As far as I can tell my issue if svelte 4 vs svelte 5 is there :
|
I think OP wants something like |
Exactly, I have started writing the code. Basically, adding a boolean
'force' to function set and internal_set, defaulting to false, if set to
true it bypass the equality test, then adding that to the parser to call
the "signal" function ( like "snapshot" ) that will call set on the value
passed. Will see if I can get it to work, always interesting to look at the
inside :)
…On Tue, Dec 3, 2024 at 3:09 PM José Pablo Ramírez Vargas < ***@***.***> wrote:
I think OP wants something like $state.signal() that marks the reactive
value as changed, which should trigger all effects associated to the
signaled state, but without having the value actually changed (skipping the
check for equality).
—
Reply to this email directly, view it on GitHub
<#14520 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAJVOGYR7T2V6HOAUUBPK732DW3RRAVCNFSM6AAAAABS5UPXK6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJUGY3DCNZYHA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Wouldn't it be better if $state.raw behaved like the state declared with let did in V4? This type of issue appears quite frequently around here One of the biggest advantages of svelte over other frameworks is precisely the fact that it doesn't need its own ecosystem and works well with vanilla libraries And one of the factors that contributed the most to this was precisely the fact that it was possible to have reactivity in external objects simply by reassigning them I agree that it wasn't a good default behavior, but as an optional behavior it was more useful than $state.raw |
I agree with you, as I have a lot of issues porting my ( quite big ) application. But still, I would rather have a signal function so that I can control myself when to actually trigger reactivity, as some state are actual props, so have a state.raw will mean you need something to declare props as raw as well. |
It's likely that currently there doesn't exist a straightforward solution but is there a way you could provide a minimal reproduction just client side only if possible? Just so there is total clarity. client side: Playground |
I feel like If this gets added it would confuse people. To me it sounds like you would be better of writing a wrapping class for your state and using a setter to explicitly run code instead of relying in sveltes reactivity system which is meant to reduce execution time cost by identifying identical values and skipping a rerun. |
I can't really write a wrapper for all my classes specially for svelte when
a simple solution like a signal function will solve all issues you can
have. That sounds like writing a lot of code (which I'm guessing a lot of
people will have to do when facing the same issue when integrating with a
vanilla library ). This is not a performance issue.
…On Tue, Dec 3, 2024, 21:41 David Plugge ***@***.***> wrote:
I feel like If this gets added it would confuse people. To me it sounds
like you would be better of writing a wrapping class for your state and
using a setter to explicitly run code instead of relying in sveltes
reactivity system which is meant to reduce execution time cost by
identifying identical values and skipping a rerun.
—
Reply to this email directly, view it on GitHub
<#14520 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAJVOGZFZUBYBR5QPUKFOUD2DYJRJAVCNFSM6AAAAABS5UPXK6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJVGUYDSOBYG4>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
I'm thinking that borrowing the idea of an https://angular.dev/guide/signals#signal-equality-functions It would only work for Upon set, instead of using Svelte's pre-defined equality function, But for other cases, upon the top level reassignment, the deeply nested properties can be compared and a decision can be made if a change occurred. It would work with the current This new option for testing equality would also be helpful for comparing NaN's where in some cases, you'd want NaN's to be equal to each other, like in |
I understand, but I still think that having a function to force trigger
reactivity is a must when working with vanilla JS libraries, which was in 4
a real strength of svelte ( and one of the reasons we decided to switch to
it ).
…On Tue, Dec 3, 2024, 22:21 Leonidaz ***@***.***> wrote:
I'm thinking that borrowing the idea of an equality function option from
Angular's signals might be a possible solution for such cases.
https://angular.dev/guide/signals#signal-equality-functions
It would only work for $state.raw(), and for $state() at the top level,
since it can be a deeply nested proxy (basically only if it's reassigned)
Upon set, instead of using Svelte's pre-defined equality function,
equals(), the provided equality function is used. In this particular case
it can just always return false.
But for other cases, upon the top level reassignment, the deeply nested
properties can be compared and a decision can be made if a change occurred.
It would work with the current $state.raw() or $state(), even with an
empty initial state, e.g. $state.raw(undefined, { equal: (prev, value) =>
false }). This second parameter would be a pojo, future proofing for any
additional options. The usage will not break any of the existing Svelte 5
code as the second parameter is optional.
This new option for testing equality would also be helpful for comparing
NaN's where in some cases, you'd want NaN's to be equal to each other, like
in Object.is()
—
Reply to this email directly, view it on GitHub
<#14520 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAJVOG3LWTO2UEFSXRJR3PL2DYOFHAVCNFSM6AAAAABS5UPXK6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJVGU3TKMBXGA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
I'm not sure I understand. Having a custom equality option would do what you want upon reassignment. Same as the E.g. let account = $state.raw(prop, { equal: () => false });
....
// to make reactive, reassign as this will call `set` and then `internal_set` which will call the custom `equality`.
account = account; |
For me, it's cleaner to have just $state.signal(account) rather than doing
account=account + having to specify an equality function for all the
bindings that needs it ? It's less code for the exact same functionality,
and is also less "magic".
…On Tue, Dec 3, 2024, 22:48 Leonidaz ***@***.***> wrote:
I understand, but I still think that having a function to force trigger
reactivity is a must when working with vanilla JS libraries, which was in 4
a real strength of svelte ( and one of the reasons we decided to switch to
it ).
I'm not sure I understand.
Having a custom equality option would do what you want upon reassignment.
Same as the force option that you were adding to the source code. But it
would allow more flexible options.
E.g.
let account = $state.raw(prop, { equal: () => false });
....
// to make reactive, reassign as this will call `set` and then `internal_set` which will call the custom `equality`.
account = account;
—
Reply to this email directly, view it on GitHub
<#14520 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAJVOG5YBHPEJDBYO2FNZWL2DYRJJAVCNFSM6AAAAABS5UPXK6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJVGYYTQMJXGE>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
We're not going to change how |
Great, what the arguments against it ? It's not a hack to use a library that is not using svelte data store to manage it's data I think ? |
Here it is, it's really minimal but does kind of show how it works now : https://svelte.dev/playground/97ab67f0c4fa405c9f11eee13ba574b0?version=5.5.3 Note, is is normal that I have to do : let theAccount = globalGraph.accounts['1']
let account = $state(theAccount)
theAccount.notify = function (cause, delta) {
account = null
account = theAccount
} instead of directly : let account = $state(globalGraph.accounts['1'])
account.notify = function (cause, delta) {
const theAccount = account
account = null
account = theAccount
} Isn't proxy suppose to proxy the object directly ? or am I missing something. It's so weird that to make things "less magic" it make them actually more obfuscated. |
The object you pass to |
Well, that is a big bummer. What would be the right approach for you when you have want to use a separate data library ? I guess wrapping everything into a big class that is declared as state and uses the Resource object from the said library - but that is adding a deep layer of glue code for no good reason I think ? |
@olivierchatry are any of these a possibility? <script>
import { globalGraph } from './GlobalGraph.js'
let name = 'world';
let theAccount = globalGraph.accounts['1'];
let account = $state({});
theAccount.notify = function (cause, delta) {
Object.assign(account, delta);
}
</script>
<h1>Hello {account.counter}!</h1> or this: <script>
import { globalGraph } from './GlobalGraph.js'
let name = 'world';
let theAccount = globalGraph.accounts['1'];
let account = $state.raw({});
theAccount.notify = function (cause, source) {
account = source;
}
</script>
<h1>Hello {account.counter}!</h1> |
There is also the following possibility: <script>
import { globalGraph } from './GlobalGraph.js'
let name = 'world';
let account = $state(globalGraph.accounts['1']);
globalGraph.accounts['1'] = account;
</script>
<h1>Hello {account.counter}!</h1> |
Yes for this example, as it is quite simple, but for reference, this is a video of our application https://youtu.be/-0myn3FsuEs?si=vwv2eDHxu1iGuRsH there is a lot of code involved and a lot of vanilla JavaScript interacting with our svelte components. If we have to redo /rewrap everything, I fear So it would be nice if at least we could trigger reactivity manually. It's actually not a lot of code ( as far as I can tell, I did the code but did not find out how to test my branch yet ). So I'm wondering why not make a small change ? |
My personal opinion is that the request for the new functionality is reasonable. Being able to trigger effects for an unchanged value is something most won't need, and that's OK because it is "extra" functionality for the minority of people that may have a rightful need to do this. I am no expert in JS libraries, but it is conceivable that there are libraries out there that, for example, produces objects that are instances of classes, and we know Svelte won't make those reactive. A potential rune As time goes by, I bet people will find more usefulness for such feature. |
Can’t you do this in user land with a simple utility? class Signal { constructor(current) { something = new Signal(…) Something.current = … (this is reactive now) |
No, wrapping all my different classes will be a nightmare. And also, it's
an anti solution: if everyone using custom data management is forced to
wrap their data into something, then their is something that needs to be
changed as you produce the same code over and over.
The solution of a state.signal is I think simple and elegant and will avoid
a lot of wrapping around.
…On Wed, Dec 4, 2024, 20:54 Abdelrahman ***@***.***> wrote:
Can’t you do this in user land with a simple utility?
class Signal {
current = $state.raw();
constructor(current) {
this.current = current;
}
}
something = new Signal(…)
Something.current = … (this is reactive now)
—
Reply to this email directly, view it on GitHub
<#14520 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAJVOG5A5NIJFMZTXCC2SUT2D5MWFAVCNFSM6AAAAABS5UPXK6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMJYGQZTGMBWGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
If you find yourself refactoring because you're unable to "force-update a variable". You might find See the recently shipped https://svelte.dev/docs/svelte/svelte-reactivity#createSubscriber. |
It is nice, but it means that I need to wrap my classes around that, and change all my code to use an attribute in the wrapper class so that it svelte knows it's reactive. This will be a lot of structural changes. Also I will add that this add a bit more of "magic" to the mix on my view, as transforming an attribute ( current in the sample ) as reactive becase you have call a subscriber on it in a separate class is really "weird". I'm guessing there is probably a nice state machine to handle these inside svelte ? whereas calling |
Here's another example (which is derived from my project in production): <script>
class A { /* in fact it's declared somewhere else which
prevents using $state inside the class declaration */
param = 15;
update() {
this.param += 10;
}
}
let item = $state(new A());
function handleClick() {
item.update(); /* implicit change happened and I know it
happened, so I need to trigger reactivity manually */
// item = item - that's what I used to do in Svelte 4 and it worked.
// In Svelte 5 I'm forced to do this instead:
let tmp = item;
item = null;
item = tmp;
}
</script>
<div onclick={handleClick}>{item.param}</div> I'm dealing with a lot of class instances in my real project. All of them have inner state which is often changed on various events or actions. I use reassigning very often to make it work. Need a better way for sure! |
Meanwhile, you can use a helper like below that uses The downside is, you need to get and set the value via <!-- App.svelte -->
<script>
import { createSignal } from './createSignal.js'
import { globalGraph } from './GlobalGraph.js'
// Usage 1
let account = createSignal(globalGraph.accounts['1'])
account.value.notify = function (cause, delta) {
account.value = account.value
}
// Usage 2
// let account = createSignal((signal) => {
// let account = globalGraph.accounts['1']
// account.notify = function (cause, delta) {
// signal.value = account
// }
// return account
// })
</script>
<svelte:options runes />
<h1>Hello {account.value.counter}!</h1> // createSignal.js
import { createSubscriber } from 'svelte/reactivity'
export function createSignal(init) {
let value
let update
let subscribe = createSubscriber((update_function) => {
update = update_function
})
let signal = {
get value() {
subscribe()
return value
},
set value(new_value) {
value = new_value
update()
}
}
if (typeof init === 'function') {
value = init(signal)
} else {
value = init
}
return signal
} |
Describe the problem
I'm using a custom made data store with realtime update. In svelte 4, when the value got updated, I was just doing
value = value
to refresh different bindings. In svelte 5 it is not possible anymore as before updating bindings, the system check if the value is the same. What more is, inside some of the object, we have our classes that are filled automatically ( think relationships ) and so it seems like svelte is not detecting the change in values.I'm not sure if I'm clear in what I'm explaining, but basically, I would need something to inform the system to update bindings forcefully.
To be a bit more precise:
The system we have in my company needs to be used without svelte, so we have our own data system. We have so call "Resource" and "ResourceCollection" ( that are basically relationships ). The system handle realtime update accross client using web sockets. To get "informed" of these change we subscribe to the resource. The only way I found to make the refresh working is by doing that (
subscribeAndDo
is internal to our system ) :Describe the proposed solution
The goal here is, whenever the props "account" change, we subscribe to it. Then whever something change in "account" the callback will be called. Here, I'm using the "trick" to make sure that svelte update the bindings. It would be great if we can have a $forceUpdate(account) instead. if that make any sense.
Importance
would make my life easier
The text was updated successfully, but these errors were encountered: