Why is this Resource fetcher called 2 times?
https://playground.solidjs.com/anonymous/347f9b68-3bae-4021-8276-00ac67404618
This log is displayed two times:
"---> why is second resource fetcher called 2 times?"Any idea as to why?
47 Replies
reading
store.value
in the setter causes it to be tracked
you need to untrack reading the storeIs it the
resource1
argument to createResource
that causes that?
I'm not sure to understandresource1
doesn't cause anything hereSolid Playground
Quickly discover what the solid compiler will generate from your JSX template
untracking the
store.value
reads fixes it because the setter is called in a reactive scope https://playground.solidjs.com/anonymous/9972ad35-46be-4305-9a6e-66e97096c6cfSolid Playground
Quickly discover what the solid compiler will generate from your JSX template
@Otonashi why wasn't the fetcher called twice for
resource1
?because it doesn't have a source
the setter isn't called in a reactive scope if there isn't a source because there's no need to refetch when it changes
but actually I need changes. But you may see in the two set of logs there are no changes. I logged the
loading
property and the accessor, and the logs are the same, twice.the change is in the value of the resource
by reading the resource via store.value in the setter, the setter tracks and reruns
Ok but I printed the value and it doesn't change
but I printed inside the resource fetcher of
resource2
resource2 not resource1
resource2 changes from undefined
and it's tracked by the setter
okay actually looking at it again maybe that explanation isn't right
regardless it is rerunning because it's tracking itself
but i need to look at the source more to understand why the case with no source doesn't have the same issue
I guess the core question now is why does it re-run when a resource has a source (it seems it is never desired to re-run when the source hasn't changed for that fetcher, as seen in the set of logs)
it re-runs because it changes itself, this is not desired
(will order my food brb)
okay so there is another condition in that the fetcher has to be synchronous
the fetcher is supposed to be asynchronous call like request to server. not sure what u meant.
well
if it's async it won't run twice
because it won't track the deep signal read in the setter
but didn't we find a code smell? and I mean for the case when the fetcher is synchronous that there is a re run
yeah
and I'm thinking DX
if it's async then it'll have left the reactive scope
anyway i think my previous explanation is correct mostly
the simple way of putting it is it's doing this
because if there's no source it won't create the reactive scope
it'll just set immediately without creating an effect (specifically computed here)
and because
setValue
tracks store.value
it reruns when store.value
changesso the
source
adding reactivity and causing the synchronous fetcher to re-run is the "problem". Is it something to change/improve in the core?no the setter shouldn't be tracking in the first place
arguably
createResource
was assuming that setting wouldn't track but anything in the shape of a signal shouldn't be tracking on sets
docs issue if anythingbut to rely on the asynchronousity of the fetcher to have an expected behavior with a custom storage like the
createDeepSignal
from the docs, this is more than a docs issuewell no it doesn't rely on async
it just assumes that
set
doesn't track
that's all
that's reasonable since both createSignal
and createStore
untrack their setters
and setting something isn't reading something
unless i'm explicitly reading a signal i don't expect a signal to be trackedI def got re-read all we said, but what is the conclusion? I have to
untrack
as you have shown?
yes
Do you think we should add that to the docs (the
untrack
)? But it's gonna be a little confusing though because the why is not easy to explain. Or maybe I can add an issue to see if the setter can be untracked always?
If I can do anything to help I will contribute.add it to the docs imo
what about untracking systematically, in the source code
i mean you could
but you wouldn't
untrack
setters in your own code
so i don't see why this shouldhttps://playground.solidjs.com/anonymous/738a8dd3-d815-4a3e-870b-dd146c80497a
Here I mutated
resource1
, but the fetcher of resource2
isn't re-run. But it should when the source changes.the tracker doesn't actually track what needs to be tracked
since you're reconciling,
resource1()
won't change unless you change it to a non-object
you need to read resource1().foo
in the source
since the fetcher is untrackedBecause reconciling doesn't change the reference of the (root) object?
basically
i mean this is what you want though
But how do you know that so fast haha
idk
thank you!
Hi @Otonashi I still have a problem with
createDeepSignal
.
https://playground.solidjs.com/anonymous/f859f3e8-f060-4c89-9491-f31f039199c8
The problem is explained in the log.
The data that the resource fetcher
fetches come from a library that holds objects. When the source signal passed to createResource
changes, I switch to another object from that library, but the problem is that it mutates the data held by the library.
Any help would be so much appreciated
(I still want to use store to benefit from nested reactivity, because this library offers to listen for changes and I update the store when there are changes with the mutate function of createResource. The problem seems to happen when I switch the object because of the source signal. But maybe there is a bigger problem with all of this that I'm missing, not sure)if the shape of the data is static then you can create an initial state with that same shape, so that the first
resolve
doesn't get used and mutated
otherwise you would have to clone the data when setting i guess, no way around it afaikI can create an initial shape if I deal with obejcts (as in my example), but what if it's an array?
with a variable number of objects in the array. or maybe that would work to initialize to empty array?
if it's an array of objects i don't think you can avoid cloning
well you could by adding objects to the array until it was at least the same size before mutating with the new data i guess but that sounds troublesome if you have nested arrays
@Otonashi do you think that we could include in Solid an option "do not use the first resolve to mutate on" / "do not mutate the initial data feeded to the store"?
Because I can't, I have to deep clone every time the objects. that's quite drastic
🤷 write a pr
i mean those still require cloning afaik
just by solid instead of by the user
I would have to deep clone here? just to make sure I got it right:
yeah
btw looking at the code again I would have done
the instruction
unwrap(store.value)
is only used when v
is a function
or maybe someone coded it like that because it's always a function here..it isn't
you can optimize it if you want, but
unwrap
is just a property access
iircstill if Solid does it, wouldn't that allow less cloning? Because the user would have to clone every time before calling
reconcile
. Whereas if Solid clones, it may just clone the first fetching and any new objects (something along those lines)yep