-
-
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
Stores working with immutable data #3584
Comments
I've created a lib for this purpose :) I had a similar performance issue and didn't want to update the entire list. Immutable option also didn't work for me either. Basically you can subscribe for only one item in the array or only one node in the object. |
I have checked it out, but I don't see how it could solve my problems. E.g. I don't see how to use it to stop svelte updating the whole DOM tree. As well, I feel that even if that would work out for one of my problems it will end-up with complicated and hard to maintain code.
I guess that depends on data you have. I had no such problem yet. Immutable data has other benefits like almost free undo/redo implementation if you need it. |
@daliusd yeah documentation is crappy right now // instead of mutating and rerendering whole object
let state= writable({arr: [
{ id:1, enabled: true},
{ id:2, enabled: true}
]});
state.subscribe(value=>{
// some time consuming calculations here (will be triggered when enabled property is changed)
// all list item components will be rerendered
});
state.update(value=>{
value.arr = value.arr.map(...);
return value;
}) // you can do something like this
let state = new State({
obj:{
1: {id:1, enabled: true},
2: {id:2, enabled: true}
}
})
// and then in each list item component
export let row;
let grayedOut = false;
state.subscribe(`obj.${row.id}.enabled`, enabled=>{
// do little calculation and only one component is rerendered
grayedOut = !enabled;
});
<div class={grayedOut ? 'gray' : ''}></div>
// somewhere in code
state.update('obj.1.enabled', false); Yes, it may look that code will be a little bit more complicated due to fact that you have grain control over what you subscribing of and what you are about to update - but it just looks like that - in real world you will know where and when some code depends on some state - it may clarify what you are doing. You can also undo / redo state here but instead of cloning whole state each time when something tiny has changed (a loooot of memory) you can store in history only what changed end reverse the process - so you can see what exactly was changed - no need to visually search what changed in sate. |
It works, if you set immutable to true in the Item.svelte. https://svelte.dev/repl/ef5c0d34d659467eb1ea7e826fb475ab?version=3.12.1 |
@PatrickG, adding this to each component would be inefficient. But anyway thank you for your comment as this pointed me to idea to add That does the trick as well. |
This example is good if all your are interested is a changing of item but I still don't see how it would work in context of svelte with {#each ...} and or beforeUpdate/afterUpdate.
Well, I will always prefer less code over more code if there are no other serious differences in performance, memory consumption or code's complexity.
E.g. let's say I have array of objects with some objects as properties: Let's assume it takes z.length * 200 bytes of memory. Now modification of n-th item in z will look like this: This will take (z.length * 8 + 24 + X) bytes of memory, where X is memory used for mutable change and 8 is assumed pointer size (and some assumptions made about JS behavior, most probably not very good). This converges to 8/200 or 4% of original object's size. If we don't want to be that optimistic we can assume 10% of object's size. This still potentially is acceptable it most situations.
|
Yeah, maybe you have right - for very simple components dot notation is useless but if you have large app, things can get messy with "native" Take a look at this example, this is a simple object - but what if app/component state had a lot of different nested nodes? |
I still don't see how deep-state-observer will solve problem with re-render of lists/array using each :-) New question: what happens if I create 100 or 1000 deep-state-observers? How expensive performance wise is it?
Normally you keep your structure as flat as possible. Here is article about redux https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape but the same principles applies to any immutable store/state. KISS.
I am working on large project using React with Redux. Usually no big problems with that. That's anecdotal evidence but still... |
https://svelte.dev/repl/a3637b5e83914c9b89f10c8cd422c747?version=3.12.1
It depends.
Yeah you keeping it flat because it is hard to work with structured data when you want this data to be immutable :D Sometimes flattening your data can lead to ugly names of properties and not quite well organized data (of course it depends). I like to have things organized and structured into proper nodes (like folders). Normalized data from your link is of course better it is grouped well, but it is easier to access and set data with dot notation - less code :P
It depends on your project and on your habits. |
Thanks for example and your responses. I have learned something new (I think others as well). |
By default svelte's stores are mutable. So you are good to go without recreating everything. |
Using {#each...} with list/array from store updates all items from list while this can be avoided with immutable writable stores. I have expected that using
<svelte:options immutable={true} />
would allow that (as per documentation "you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changed" here https://svelte.dev/docs) but that's not the case.Here I have created sample https://github.com/daliusd/svelte_immutable_store app to demonstrate the problem. Run it, open dev tools and modify text or or add new item. You will see that
afterUpdate
(https://github.com/daliusd/svelte_immutable_store/blob/ebc1bd4bd00a3d4a7687f5b13d484fd0135e6a1d/src/Item.svelte#L8) is called for each item even if we are updating only single item.Here you can see https://github.com/daliusd/svelte_immutable_store/blob/master/src/store.js that I do not mutate my data thus references will not change.
Describe the solution you'd like
I think writable store should respect
<svelte:options immutable={true} />
. I think this might be not enough however as internal svelte code might not handle lists/arrays properly. I have not looked that deep into Svelte's code.As well I think that by default svelte should create mutable stores (as it is now).
Describe alternatives you've considered
In some old version svelte supported immutable stores (see #1146). But I assume that was removed somewhere in progress. I think respecting global setting is better option as that would make app consistent and developer shouldn't think if this store is mutable or immutable.
How important is this feature to you?
Modern browsers seems to handle updates that do not change anything quite well. E.g. if you change div's position to the same value there will be no physical re-render. That makes this problem quite low priority IMHO.
From other side in one app of mine I do canvas re-render after update. That means I can not do re-render after each update because afterUpdate (or whatever is reactive) is called for all items even if I update only one item. I found workaround for this problem but it is second workaround I have to do in Svelte (bug is reported for first one already by other people).
Additional context
As well I see #2171 which explicitly states that stores are always mutable.
Lastly, in svelte's source code I see those functions. That's just my curiosity:
Now I wonder why we have this part
a != a ? b == b : a !== b
.a != a
is true only for NaN, that means this line overrides/inverts NaN behavior. Is that intentional?The text was updated successfully, but these errors were encountered: