Reactivty and prepareDerivedData
From @Wasp
So I'm using prepareDerivedData to, well, derive some data for my items; some of them are tied to each other (ie, an "equipment" type item affects a "skill" type item's bonus). I was hoping that re-calling the derived data methods would cause the TJSDocuments to pick up the alterations, but since it's not a database operation, it doesn't appear to pick that up. Is there a good way allow one item to cause the reactivity of another without manually creating stores?Moving discusion to forum post.
16 Replies
You could create event listeners on the prepareDerivedData, the components could pick up the signals when prepareDerivedData is called and update their local values
I definitely think Svelte 5 is going to provide a lot of flexibility in the near future for cases like this. IE all of your derived data can define
$state
and accessors.
Right now there is a circuitous hack to force an update. I don't really want to make the TJSDocument store update mechanism public. I can work out the details of that with a code example. It involves faking a render call to the registered "app" from TJSDocument in the underlying document.
The other mechanism is using stores for derived data which is not as horrible as it might sound with the writable-derived
library found in TRL.
Example code:
You'll notice if you have been following Svelte 5 and Runes that this setup is very similar except the getters for derived data are not reactive and in your Svelte components you'll have to retrieve the derived store vs usage of accessors. This pattern is nice though because when Svelte 5 is available you can replace the usage of writable-derived
with $state
.
I have tested things out in Svelte 5 and it's less boilerplate, but still a bit circuitous due to $state
only working for local variable declarations. The goal of this Svelte 5 demo is to be able to define explicit getters / setters on a class vs dynamically defined for types / documentation and such.
Svelte 5 example:
https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE32S626DMAyFX8XKJhW2iq5_6UWqtLcY-0HB7dJBghLTbUJ59-VCuWjVKqHi5Hz2ORYdO_EKNUvfOibyGlnKDk3Dlox-GlfoK1aEttayVYU72epC8Yb2mciI141UBB0cCpIKDJyUrGGRrHydXPRiY2VWWEihCXKv2oHAr0BEsb3frsaOYntsiaQAKdKi4sXnroti2O0DmlzzqsW17TArn2ENxvvxiE6hm94bWM0OXmV7rNC4wWHY3sarZclPHEuWkmrRLMdtOHDcx0VPd4HfPn9R5Vr3O-icj4cSFb9iOU2v2pA4KDKqkGDI86gpJ4xe3D7A_obLYNYp-pZRzzz1cOxnOIY-uE5uky3Rz8noPEyK4g4UUqtEf7ABs7zJ9Cjzf9bpaNC_TNXnuUOXa9Y6HFsiAMbZJOOcumdqaQRnCZLBYQh4z949YGI2gP84vcNPfJu_38W7-QXn2MclMQMAAA==
I wonder if any advances in the compiler can occur to support explicit accessor definition instead of this middleman / double definition approach. It's less code than the example provided above and removes the annoyance of having to work with stores.
The svelte component is nicer though:
Thanks guys, stores ended up being the best way to do it 😄
Just remember to stay away from the
get
function found in svelte/store
as it is not performant.Hm, I've set it up like you've suggested in Svelte 4, and if I do the following in my items;
The console.log provides what I expect;
{subscribe: Æ’, set: Æ’, update: Æ’}
, but the set throws store.subscribe is not a function
? specifically: https://github.com/Haxxer/FoundryVTT-Leobrew/blob/New_Leobrew/src/documents/item/item.js#L52-L53
I did post somewhat pseudocode which could be the problem. I was basing it on TJSPosition: https://github.com/typhonjs-svelte/runtime-base/blob/main/src/svelte/store/position/TJSPosition.js. This is a complex custom store w/ children stores, so it may not be the easiest to follow. It is a pattern that I use in several other areas though.
Perhaps run your code w/ a production build and grab the stack trace from the browser which should point to the files / source in a likely more clear stack trace. In the stack trace above all of the references to
index.js
I assume is the bundled output. The at Array.map (<anonymous>)
line is somewhat suspect, but not sure where it is located presently.
I'll also come up with a Svelte REPL example that can be confirmed and show this general pattern independently.Hm, it seems that when I
set
the propertyStores
, the underlying subscription method treats the item itself as a store? I'm a bit confused, hehI suppose that my item class would need a subscribe method, lest it won't know how to handle it
I don't believe you can pass multiple callbacks ala
...callbacks
though that is probably not the issue you are tracking down.I... am not
I am simply calling
set()
on the propertyStore
defined as propertyStore(this, "bonus")
, like your example
Once it is set, that underlying svelte method is run, treating the item itself as the store, for some reason.
I'm not calling subscribe()
myselfOops... Yeah... Re the
propertyStore
thing... Definitely brain farted there. TJSPosition which I was grabbing the pseudocode for is a store w/ the child stores.
You can still use this approach though and I do in other areas slightly differently. Check out SvelteReactive: https://github.com/typhonjs-fvtt-lib/svelte/blob/main/src/application/internal/state-reactive/SvelteReactive.js#L576-L604
For the actor / items you'd change things to:
This general approach allows you to store the data in an organized manner / efficient access for the getter accessors and still benefit from creating the stores for reactive access.Yeah, I delved into the TJSPosition and saw the discrepancies, hence my confusion! Thanks for clearing that up 😄
Ayo, great success!
Awesome... So everything is working well I take it. 😄
The nice thing about the getter for
stores
is that you can use destructuring to access them in one statement.I lurv myself TJSDocument & reactive stores; setting up a chain of sheet rerenders just to get the skill list to refresh when external documents change would have been a nightmare
Right on... I can't wait to get the other half of TJSDocument complete especially w/ the Svelte 5 angles to it.
Just have to try and convince kgar to give things a look. I'm making a new TRL release soon that thoroughly handles PopOut / separate browser window for existing components / focus management / browser access (clipboard / eye dropper) w/ reactive monitoring of the active window possible that is easy to access. It would be not so great to try and custom code a bunch of stuff for all of that.