Replies: 2 comments 2 replies
-
This a lot to answer so if I miss something, feel free to point that out. First off: Firebase + vatio: good idea? That's a matter of opinion really. It sounds like you're storing you're entire state in firebase though, which already does live updates, right? While it's not really an issue to use valtio in addition, you should ask yourself why you're doing it in the first place if it works just as well without it. First question: valtio doesn't really have a strong opinion on how to store the state, so it's really up to you. I can tell you from experience, however, that even though valtio does a great job with recursively proxying child objects, the flatter your state, the easier your life will be. These days I tend to split up my state into smaller individual states as it's usually easier to keep track of. For example: const stateProxy = proxy({
games: {
byId: {}
},
heroes: {
byId: {}
}
}); I would probably do this instead: const heroesProxy = proxy({
[heroId]: {}
})
const gamesProxy = proxy({
[gameId]: {}
})
// can combine them if you want
const store = proxy({
games: gamesProxy,
heroes: heroesProxy
}) Once again though, it's a matter of opinion. Do what makes the most sense for you and your app. Second Question: I don't meant to sound like a broken record, but once again, do what makes sense for you. Since it's your own project, if you understand how Third Question: The snapshot you're taking is going to be update anytime the stateProxy is updated at all. Not the greatest of solutions. If you're wanting to reduce rerenders (and why wouldn't you want to do that), just pass in the object/ array that you want to keep an eye into the useSnapshot function. E.G. function Items() {
// useHeroRef provides the id of the hero currently shown from context
const heroRef = useHeroRef()
const hero = useSnapshot(stateProxy.heroes.byId[heroRef]);
...
} valtio proxies all objects recursively underneath - so you can pass any of them into the snapshot and it will limit when it re-renders based on what object you pass to useSnapshot. Hope this helps. |
Beta Was this translation helpful? Give feedback.
-
Thank you for responding in such detail. On the topic of Firebase + valtio: my main reason to not rely on Firestore alone is, that even if I subscribe to individual documents, each change in such a document would re-render the whole component for say a hero (and that is a pretty complex thing made out of many different moving parts, because there are actually quite a lot of properties to track). That is, if I would just hold the hero in a context variable and use that everywhere. Maybe there's a better way to do this that I'm not aware of (without using additional libraries). So my goal is to have fine-grained control over which parts of the state I subscribe to in each location. First two answers: sounds good, my main reason for asking those questions was to make sure that I don't do anything that actually breaks the functionality in ways that might not be obvious immediately. I'm not a professional front-end engineer, so while I think I understand the different concepts of valtio, zustand and jotai (to name if few), I don't have the experience to immediately grasp the consquences of the design decisions. Third answer: that actually does not work for me. When I use Definitively helped already :) |
Beta Was this translation helpful? Give feedback.
-
Hi,
I'm currently developing a companion app for a board game (as a private pet project) and I'm building a react based frontend on top of Firebase and I'm using Firestore as my state store. In there I have two collections right now, one for storing the states of different games and one for storing the states of different heroes that each have a reference to one of the games. The data model is quite flat, so for the most part I'm dealing with primitives, objects with primitives or arrays of primitives on the top level. Here is a subset of the hero:
In addition to the state data in Firestore there is static data that contains information about game components and concepts that comes with the app (plain javascript objects stored by id in a kind of dictionary) and I just point to the needed data with its id (e.g.,
roleReference
in the example refers to a specific object in the roles dictionary that contains data about that role like the name).Now valtio comes into play. I have a global state proxy that looks (for the most part) like this:
When an authenticated user opens the page, I subscribe to the two collections and update the state like this:
Any change to the hero through the user is immediately written to the database and not dealt with in the app itself (e.g., if the name of the hero is changed, I just send an update with the new name to Firestore, which will then update the hero in the state through the subscription to the collection, which finally will update the UI). And just to be clear, the input itself is of course backed by local state and only 'saving' the changes will trigger this update.
Doing this and then using useSnapshot in the different components that show the hero state generally works quite well and the reactivity is also mostly working as expected. The component that just renders the name only re-renders when the name changes, even though each update on the hero basically replaces the whole hero object in the state.
First Question
Is this the right way to update the state? Due to replacing the whole hero object, I was wondering whether I need to wrap
change.doc.data()
into aproxy(...)
call or does the stat proxy do this automatically (trying it out did not make a difference at first glance).Second Question
I actually want to augment the data from Firestore with additional, computed information. Looking back to the example, I not only want to have the mentioned
roleReference
but also the resolvedrole
in my hero object, so that I don't need to look up the role every time. What would be the best way to do this? Some ideas:or
and the state update call would look like this:
There are also use cases where I would need to do some calculations (like summing up a few numbers from different parts of the state) and it would be nice to do this once and have it available and reactive in the state. Do the approaches even differ from valtio's point of view, since it only gets the augmented object anyway? Does it make sense to use
ref
in the getter approach? (as said, the role information is part of the static data that does not change)Third Question
How to deal with arrays and re-renders? Example:
This is re-renders the whole list on every change (which in general is not a big issue, I just want to understand what options I have). Another try:
This reacts to changes of the length of the array and re-renders the whole list accordingly, but a change to one of the items does not trigger a re-render. I suspect that I destroy the subscription on
item
in theItem
component with completely replacing the hero object in the proxy, which seems to be confirmed with the following example (that works as expected):the difference being, that I snapshot the whole stateProxy and then just work my way to the item instead of snapshotting the item proxy itself.
This seems to be the expected behavior, but the general approach of overwriting an object in the state proxy feels a bit like misusing valtio for something that it is not made for and I would love to have some opinion on that. Is it fine to just always snapshot the stateProxy object to be sure to capture all changes? Are there drawbacks or pitfalls with that (besides the ones I already came across)?
General Considerations
As I said, this is a hobby project and I've been working on it for quite some time because I also use it to test out new technologies and take my time to find the right setup. I have tried various state libraries (including zustand) and so far, from a usage perspective, I like valtio by far the best. Most of my time on the project has been and will be spent writing components that visualize the hero's state (and other state objects), so I want that experience to be as streamlined as possible. The fact that I can just grab my snapshot, render the information I want, and rely on valtio to subscribe to exactly the right parts of the state is such a breeze. For me, this is superior to writing specific selectors for most components in zustand and many of the approaches of other state libraries (although I still like zustand quite a lot).
So, it would be nice to get some feedback on my thoughts and questions. Is this a reasonable approach or am I completely crazy? What could be improved?
Thanks in advance!
Beta Was this translation helpful? Give feedback.
All reactions