Svelte store assignment calls default writable().set and then custom .set?
This is expected behaviour! Forget about stores for a moment, and — since state.set
is a noop in your example — remove that line:
<script>
// import { state } from "./stores.js";
let pojo = { a: 0, b: 0 };
pojo.a = 1;
pojo = {d: 0};
//state.set({c: 0}); // no effect, as expected
</script>
<h1>state: {JSON.stringify(pojo)}</h1>
The outcome...
state: {"d":0}
...is exactly what you'd expect.
When we replace pojo
with $state
, we're still assigning to (or mutating) a local variable, with the same reactivity characteristics. The only difference is that we're also calling state.set
. Ordinarily that would cause a change in the store value that would be reflected to its subscribers, but since your custom store doesn't do that, we're left with the value following the last time $state
was touched by an assignment operator.
In the JS output of your REPL, you can see the following:
function instance($$self, $$props, $$invalidate) {
let $state;
component_subscribe($$self, state, $$value => $$invalidate(0, $state = $$value));
set_store_value(state, $state.a = 1, $state); // changes the value of state?!
set_store_value(state, $state = { d: 0 }); // ''
state.set({ c: 0 }); // no effect, as expected
return [$state];
}
Using the shorthand reactive assignment $<store>
compiles into set_store_value()
calls, which is a svelte internal method defined thusly:
export function set_store_value(store, ret, value = ret) {
store.set(value);
return ret;
}
So the assigned value is, in effect, passed on to your store's set
function, as you would expect.
However, you can see in the JS output above that the value ultimately returned is a local variable called $state
(the $
sign there is not a reactive modifier, just part of the name). During those set_store_value()
calls, you can see that this local variable is assigned the same value that is passed on to your store's set
method (in fact, the local variable is assigned that value, and then is itself passed on to the set_store_value()
method):
set_store_value(state, $state.a = 1, $state); // changes the value of state?!
set_store_value(state, $state = { d: 0 }); // ''
I expect this behavior to be some sort of optimistic look-ahead/resolution.
Perhaps it is implied in the Svelte store contract that a value passed to a store's set
method must in fact modify the store accordingly, in which case the optimistic resolution approach would always yield coherent results?
Hopefully Rich Harris (or another Svelte contributor) will see your question and provide a more definitive answer.