-
I'm looking at moving our ~large-ish app over from mobx to valtio, with the idea that However, because we use ~per-page/per-form stores (instead of a global store), a lot of our code just passes mobx stores/proxies around as props: function AuthorList({ store }: props) {
return <Observer>{() => {
// AuthorView will reactively read/write the `a` the child mobx proxy
return store.authors.map(a => <AuthorView author={a} />);
})}</Observer>
}); Or our super-reusable function TextField({ field }: props) {
const onInput(text) = field.value = text
// wrap in Observer...
return <div> label boilerplate, errors boilerplate {field.value}</div>
} I.e. code that really can't "just import the store from a static / module variable". I'm really excited about valtio removing the function AuthorList({ store }: props) {
const snap = useSnapshot(store);
return snap.authors.map(a => <AuthorView author={a} />)
}); But now the So that makes me think I should pass the function AuthorList({ store }: props) {
// don't bother with useSnapshot? obviously need it for reactivity...
// const snap = useSnapshot(store);
return store.authors.map(a => <AuthorView author={a} />)
});
function AuthorView({ author }: props) {
// _now_ useSnapshot
const snap = useSnapshot(author);
// but I can still mutate the store when needed
const onClick = () => author.clicked++; ...but now I'm susceptible to screen tearing, b/c I might have ~100 authors and the first 50 get v10 of their snapshot, but the last 50 get v11 (like if some global operation completed between the first 50 & second 50, which changed some field on all authors). Granted, I really don't know if this is a big deal or not, i.e. how often it happens in practice/should I really care? It seems like React's core team thinks it is a big deal, given they added But, It makes me wonder if React could provide a "global frame count" sort of number, that works similar to like a game drawing frames on the screen; it draws frame 1, then frame 2, then frame 3. Then Component A's ...that thought experiment aside, without React providing this "the current frame number is X" for multiple ...but then also pass the store, for when I want to do mutations. But something like Do you have any thoughts/recommendations on this tension? That snapshots must be created once high-up, but leaf components will still want the store. My only current "...maybe good? not sure..." idea is that if Ha, so after thinking that through, now I am (slightly) worried about stores & snapshots looking "too similar"; let's say an engineer writes: function AuthorList({ store }: props) {
return store.authors.map(a => <AuthorView author={a} />)
}); And I say "no, sorry, that's not reactive, you need to go through function AuthorList({ store }: props) {
const snap = useSnapshot(store);
// i'm good now!
return snap.authors.map(a => <AuthorView author={a} />)
}); Good! But then they write interface AuthorViewProps {
// no indication of "author store" or "author snap"
author: Author;
}
function AuthorView({ author }: props) {
// i was told to always useSnapshot to get reactivity!
const snap = useSnapshot(author);
return <div>{snap.firstName}
}); They have re-snap'd the snapshot. :-/ ...I dunno, should Maybe I just want a The rationale is that if I'm passing stores as props anyway (and using many per-leaf-component Granted, a naive Approaches:
Hrm. Fwiw I'm going to hit "Start discussion", but I realize this is a super long question; definitely np if you just scan it, not respond right away, and let me sit on it for a bit and mull things over; even just writing up the discussion to guide my own thought process has been very helpful. :-) Thanks! (Fwiw I tried to scan the some existing Valtio-based apps to see if they had an obvious answer for this/patterns to follow; I scanned your remote-faces sample app, but it seems like it's using module-level states + per-component |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
Hi, I didn't read everything, but one note is, to make it reactive, we useSnapshot and then pass a store. function AuthorList({ store }) {
const snap = useSnapshot(store);
return snap.authors.map(((a, i) => <AuthorView key={a.id} author={store.authors[i]} />)
}); It's a bit tricky but should work the best. (There should be other solutions.) |
Beta Was this translation helpful? Give feedback.
-
...really? I thought the idea with concurrent rendering is that we might have:
...unless the behavior of React concurrent mode is not that "the 100 component-tree will have their render-to-build-VDOMs interrupted" but merely that "building the VDOM is still done synchronously, but reconciling the VDOM to the DOM is done concurrently". Hm, no, the React docs heavily insinuate it render/VDOM building itself that is concurrent. Or maybe the VDOM building can still be interrupted, but after interrupting and going back to finish "the last 50 components", React quickly asks the 1st 50 (via each of their Damn. I bet that is it? If so, that is amazing, and I apologize for you having to lead me to that conclusion through a long Q&A ticket. I swear I've done a non-trivial amount of googling on useSyncExternalStore/React concurrent rendering/etc, but haven't found anything that explains the "how it actually works" like I described above. |
Beta Was this translation helpful? Give feedback.
Hi, I didn't read everything, but one note is, to make it reactive, we useSnapshot and then pass a store.
Also, having
key={}
would really help in React. So, I'd assume author hasid
prop.It's a bit tricky but should work the best. (There should be other solutions.)
You also want to wrap
AuthorView
with React.memo.