Closed dpmccabe closed 1 week ago
By "batch", do you mean something like a derived store that always waits til the next tick before updating its value? An async store like that is something that has been discussed before, but I'm not sure that it has its own issue.
But in any case, this is something that can happily live in userland in the meantime. The store implementations that come with Svelte are basically just blessed userland code. It's the store contract that's the important part. It's not too difficult to write a derived store composition function (or any other custom store) that has precisely the characteristics you need for a particular problem.
Yes, I suppose it could be a variant of the derived store that waits for the next tick. Alternatively, there could be an explicit batch
function like in the linked SolidJS example, which might be useful if the updates you're making affect multiple derived stores.
I did find this alternative store, which claims to solve the "diamond" update problem (as it's described there). I'm trying it out now.
Edit: That store library is actually solving a slightly different problem. Leaving my issue here open as a feature request.
@dpmccabe You can use @amadeus-it-group/tansu instead of svelte/store
. It has a compatible API and, in addition, has a batch
function that can be called to delay updates synchronously:
batch(() => {
$searchText = searchEl.value;
$page = 1;
})
Cf the following updated REPL: https://svelte.dev/repl/445ef0a0ce684a47ac67222c686784f5?version=3.50.0
Note that the "diamond" problem you are talking about is also solved in @amadeus-it-group/tansu
(cf this pull request).
Wow, thanks @divdavem. I've been looking for something exactly like this for a while!
you can create a store to subscribe them in batches
const batch = (...stores) => {
const values = stores.map(a => get(a))
return readable(values, set => {
let requestId
stores.forEach((s, i) => {
s.subscribe(v => {
if(values[i]!==v){
cancelAnimationFrame(requestId)
values[i] = v
requestId = requestAnimationFrame(() => set(values))
}
})
})
})
}
then:
const results = derived(batch(searchText, page), ([theSearchText, thePage], set) => {
https://svelte.dev/repl/aa26bf2b4cb247e3a60f02d0c6e4757b?version=3.50.0
Yes, I suppose it could be a variant of the derived store that waits for the next tick. Alternatively, there could be an explicit
batch
function like in the linked SolidJS example, which might be useful if the updates you're making affect multiple derived stores.I did find this alternative store, which claims to solve the "diamond" update problem (as it's described there). I'm trying it out now.
Edit: That store library is actually solving a slightly different problem. Leaving my issue here open as a feature request.
I'm the author of the @crikey libraries you mentioned. You are correct that the diamon dependency problem I describe is slightly different.
Another way you could go is to store the value as a single object.
{ searchText: 'xxx', page: 1 }
You could then change this composite value atomically and avoid any need for batching.
You could either manage this composite value manually, or use something like @crikey/stores-immer and @crikey/stores-selectable to help you.
Closing since Svelte 5 with $effect
etc provides a way to do this, and it's also possible to debounce updates in the store world.
Describe the problem
(Copied from my stackoverflow post)
I have a Svelte app with a relatively complex reactivity model. Data for different parts of the app is loaded from an API based on the state of potentially many parts of the UI, and changing one part of that state often requires simultaneous changes to other parts.
A simple example would be a UI with a search box, loaded results, and pagination. If you search for something, click through a few pages of results, and then change your search query, the new results should start back at page 1.
In terms of Svelte stores,
results
is a derived store with dependencies on writable storessearchText
andpage
:REPL: https://svelte.dev/repl/17ac70e4d4144616aa8af5c01e67a4cc?version=3.50.0
Note what happens in the console if you enter a search term, advance the page, and then search for something else:
You can see that when the search text is changed from "a" to "b", the derived store triggers an unnecessary update for page 3 of "b". While this makes perfect sense (nothing in my code says that the updates to the two writable stores should happen simultaneously), it's obviously inefficient and in my case causes an extra network request.
Describe the proposed solution
Other reactivity libraries like SolidJS, effector, and Preact Signals include utilities that let you batch updates to stores, effectively solving this problem.
Is this something that could be easily added to Svelte? Right now I'd be thrilled just to have a utility function I could drop in to my app to approximate other libraries' batch update solution if this is possible.
Alternatives considered
searchText
andpage
into a single writable object. That kind of design pattern isn't going to be sustainable or efficient in a more complicated app as "global" state objects must become more numerous/bigger.$:
blocks instead of stores, which again isn't a good solution for complicated apps where reactive data is extracted from components into external JS/TS files.Importance
would make my life easier