Skip to content

Surgically update objects without causing mass reactivity #4535

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

Closed
purebordem opened this issue Mar 10, 2020 · 19 comments
Closed

Surgically update objects without causing mass reactivity #4535

purebordem opened this issue Mar 10, 2020 · 19 comments
Labels
awaiting submitter needs a reproduction, or clarification compiler Changes relating to the compiler feature request temp-stale

Comments

@purebordem
Copy link

Is your feature request related to a problem? Please describe.
Updating a key in an object results in re-firing all reactivity associated with that object, even if the associated reactivity did not specifically reference the specific key in the object that was changed. Any update to an object, even if the exact same data currently in the object, will also cause a re-fire. This does not happen with basic variables.

Describe the solution you'd like
Only fire reactivity associated with the specific key that specific key is updated.

Describe alternatives you've considered
Have tried to develop a custom store to handle surgical updates, but currently cannot determine a valid method.

How important is this feature to you?
Rather important. I need to maintain state sync between multiple clients and the server and passing it back and forth as an object would make things much more straightforward. Currently resorting to using basic variables.

Additional context
REPL demonstrating object reactivity behavior vs standard variable.

https://svelte.dev/repl/8661837c43214b0e941661ab7eebac1d?version=3.19.2

Other
I may have a fundamental mis-understanding on how the reactivity works and what is possible. That said, I really like the way it feels to write Svelte and the object reactivity behavior currently feels counter-intuitive.

@shirotech
Copy link

shirotech commented Mar 12, 2020

I think you can try setting the immutable option to true which should disable the check for mutating objects.

@purebordem
Copy link
Author

@shirotech I tried toying with that today and can't seem to get immutable to delivered the desired behavior either. It really seems like the reactivity is strictly at the object level. Change one thing, it re-fires it all.

@raythurnvoid
Copy link
Contributor

As a workaorund you can try this:

$: prop1 = obj.prop1;
$: console.log("This will fire only on prop1 change", prop1)

@purebordem
Copy link
Author

As a workaorund you can try this:

$: prop1 = obj.prop1;
$: console.log("This will fire only on prop1 change", prop1)

Neat idea. I ultimately ended up creating a custom store using lodash that implemented the required functionality. Still would be nice for it to be native without need for a custom store.

@student020341
Copy link

student020341 commented Feb 19, 2021

Hey @raythurnevoid I found a scenario where this doesn't work. Or if you could help me with the workaround I would appreciate it! If one of the object props is an array, the update refires for another prop being updated. See here:

https://svelte.dev/repl/69a716924f37430ea28fafad92b2debe?version=3.32.3

In the console, you can see the event firing when the string prop is updated even though the array prop is the one being watched. Or is it expected that the workaround will only work for a flat object of simple properties? Google brought me here because I'm facing an issue like this now.

edit: A little more testing info

in that repl, you can change scales to a number or string to see the issue go away. If you make it an array or object the issue is present.

@raythurnvoid
Copy link
Contributor

raythurnvoid commented Mar 5, 2021

@student020341 The behavior is correct, the fact is that Svelte (but also other frameworks) cannot know if a prop of an object or an item of an array is changed since it should perform a deep check that is bad for performance. As a workaround for this problem Svelte by default chose to assume that the variable has been changed even if it's not.

To prevent this you can use <svelte:options immutable={true} />.
This will tell Svelte to run reactive code only when the instance of a variable is changed. (To understand each other, an instance of a variable is updated when this check return false: newVal == oldVal).
In case of objects and arrays, to update the instance you must re-assign and re-create the variable. Eg: tuna.scales = [...tuna.scales].

@stale
Copy link

stale bot commented Jun 26, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale-bot label Jun 26, 2021
@stale stale bot removed the stale-bot label Jun 26, 2021
@stale stale bot removed the stale-bot label Jun 27, 2021
@dummdidumm dummdidumm added the compiler Changes relating to the compiler label Jul 11, 2021
@derolf
Copy link

derolf commented Nov 14, 2021

As a workaorund you can try this:

$: prop1 = obj.prop1;
$: console.log("This will fire only on prop1 change", prop1)

Neat idea. I ultimately ended up creating a custom store using lodash that implemented the required functionality. Still would be nice for it to be native without need for a custom store.

What did you do?

@aradalvand
Copy link

aradalvand commented Dec 23, 2021

Does anybody have any rough idea how tricky or complex this would be to actually implement?! I'm not familiar with Svelte's internals.

For a tool like Svelte that is known for being a highly-efficient UI framework that generally avoids causing "mass reactivity" and does everything "surgically" as much as possible, a promise which it actually does fulfill beautifully in most cases, this particular behavior is disappointing, I think.

Also, the reason why this issue doesn't have that many upvotes is likely because this behavior is not obvious, unless you dig a little deeper into things, but not because it's trivial.

@rotimi-best
Copy link

rotimi-best commented Oct 8, 2023

I am having this issue as well.

In my case I have an object like this:

let lesson = {
  materials: { note: '', slideUrl: '' },
  comments: 0,
}

Then I have an autosave function that ideally should only be called when lesson.materials changes, like:

$: autoSave(lesson.materials)

However when I change lesson.comments = lesson.comments + 1, my autosave function is called. I didn't expect this behavior, it took me about an hour to realise this was the case.

From the above conversations there is no work around, and this issue has been open for about 3 years now.

Can anyone please help with the issue?

Demo: https://svelte.dev/repl/d549cce875bc4b368b96135da2fa67a1?version=4.2.1

Screen.Recording.2023-10-08.at.15.24.35.mov

@dummdidumm dummdidumm added this to the 5.x milestone Oct 8, 2023
@aradalvand
Copy link

aradalvand commented Oct 8, 2023

Sub-object reactivity is something that even v5's runes don't seem to solve yet last time I checked, hopefully it is addressed before v5 is released.

@xamir82
Copy link
Contributor

xamir82 commented Oct 8, 2023

I'd love to see this fixed in v5 too. Sometimes it makes semantic sense to group state items together under an object, but doing so currently causes the reactivity system to behave differently and that could be surprising/undesirable.

@benmccann benmccann modified the milestones: 5.x, 5.0 Nov 17, 2023
@aStarlikeMine
Copy link

I'd be interested in this too as we recently hit this unknowingly.

@joaquimnetocel
Copy link

Am I understanding it wrong or Svelte 5 runes solves this problem?

Take a look at the example above rewriten with runes:

REPL REWRITEN WITH RUNES.

@aradalvand
Copy link

aradalvand commented Dec 12, 2023

#9739 now solves this! 🥳

@joaquimnetocel
Copy link

Then, could we close this issue?

@Zheoni
Copy link

Zheoni commented Dec 12, 2023

This issue is with the current reactivity system (not runes), and if it will still exist in v5, this should be fixed.

@aradalvand
Copy link

aradalvand commented Dec 12, 2023

this should be fixed.

It probably won't. If you want sub-object reactivity, use runes. The old reactivity system is planned to be deprecated in the future.

@joaquimnetocel
Copy link

this should be fixed.

It probably won't. If you want sub-object reactivity, use runes. The old reactivity system is planned to be deprecated in the future.

That is why I think the issue should be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting submitter needs a reproduction, or clarification compiler Changes relating to the compiler feature request temp-stale
Projects
None yet
Development

No branches or pull requests