Creating Sub-stores / MVVM Pattern (Please critique!) #3302
-
Project / Concept:I've been considering Zustand as a possible candidate for implementing ViewModels. When it comes to more complex applications, one of the struggles is maintaining a tree of state for a tree of components, but in one place. Example App:
Component Tree vs. State Tree:
In this example, I have an item list that can grow and shrink in size. Each of those item components needs to have state. A naive approach could be to pass each component an initial state, and let the component take over control of the state. But this has obvious drawbacks, like not being able to read or update the child's state from parent components. However, such an approach is common, because it can get very complicated to pass a tree of state downwards, especially when the component + state tree is deep, or there are lists within lists. The MVVM pattern encourages fully separating the state from the UI. A ViewModel connects the server (where true model is stored) to the UI, providing state updates when the model changes, and receiving and dispatching model updates from UI events. When I first considered implementing such a ViewModel, I immediately thought of Zustand because it makes this separation pretty intuitive. But the one thing Zustand lacks is the innate ability to create 'sub-trees' of states. In this project, I aimed to fill that gap by implementing a Here's my example of this sub-store system. The top image above shows a picture of the simple app, and the one below illustrates the three levels of stores - the root Zustand store, the ListItem stores, and the Tag stores. To demonstrate its effectiveness, I included an 'Add Tag to All' button to show that updating child state from the parent works seamlessly. Furthermore, I memo'ed the list and tag components to show that, with this small memo optimization, only the components that use changing state will re-render on state updates. To see this effect, turn on debug mode. (As a note, the memo is required because the list component always re-renders since it depends on the list of children, which always changes if any child changes. But this is much better than re-rendering every component in the entire tree.) Afterthought / FeedbackWith that said, I'm really looking for good critique on this appproach. Here are some of my questions:
Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
Ok, I did some deep thinking, and came up with - I think - a much better approach. I hate to say it, but I did end up kicking out Zustand with this approach. With that said, it's pretty incredible, and makes state management as easy as mutating plain JSON in Javascript. It uses a tree of proxies, tracks 'get' requests while rendering, and triggers re-renders when 'set' is triggered in the proxies. The only requirement is to wrap functional components that use state with a new I re-implemented the same app as before using the new system. As you'll see, modifying state is as easy as pushing / splicing arrays, assigning properties to objects, etc. Components using state will automatically react to changes they use, and nothing else. Another win is that modifying data deep in the state tree doesn't require replacing all objects/arrays up to the top-level. The object values remain stable, but reactivity still works seamlessly! |
Beta Was this translation helpful? Give feedback.


Ok, I did some deep thinking, and came up with - I think - a much better approach.
I hate to say it, but I did end up kicking out Zustand with this approach. With that said, it's pretty incredible, and makes state management as easy as mutating plain JSON in Javascript. It uses a tree of proxies, tracks 'get' requests while rendering, and triggers re-renders when 'set' is triggered in the proxies.
The only requirement is to wrap functional components that use state with a new
reactivefunction, similar to React'smemoorforwardReffunctions.I re-implemented the same app as before using the new system.
As you'll see, modifying state is as easy as pushing / splicing arrays, assigning prop…