I'm using mobx-state-tree and mobx-react-lite, can someone guide me to a better pattern,
wishlist.js - wishlist store
import { types } from 'mobx-state-tree'
export const WishListItem = types.model('WishListItem', {
name: types.string,
price: types.number,
image: "",
}).actions(self => ({
changeName(newName) {
self.name = newName
},
}))
export const WishList = types.model('WishList', {
items: types.optional(types.array(WishListItem), []),
})
root.js - root store
export const RootStore = types.model('RootStore', {
counter: types.optional(Counter, { count: 0 }),
wishList: types.optional(WishList, {
items: [{ image: '', price: 10, name: 'Yoda' }]
}),
})
I'm updating the store as
setInterval(() => store.wishList.items[0].changePrice(Math.random() * 100), 500)
In my Wishlist view wishlist.jsx
const WishListItem = ({ image, name, price }) => {
return useObserver(
() =>
<div>
<img src={image} />
<h3>{name}</h3>
<h5>{price}</h5>
</div>
)
}
const WishListView = ({ items }) => {
return useObserver(
() => <>
{
items.map(
(item, key) => <WishListItem {...item} key={key} />
)
}
</>
)
}
export default () => useObserver(() => (
<WishListView items={store.wishList.items} />
))
Here I have to use useObserver
or Observer
at every level of the component tree, to make it reactive, is there any way to pass a reactive reference to the child?
It works perfectly fine with primitive types like string or number, but with an array or an object, I have to either directly refer changing variables at the parent like store.wishList[0].price
or use useObserver
in the whole tree.
I want to pass the items array to children, and update children on the changes, just this at the root
export default () => useObserver(() => (
<WishListView items={store.wishList.items} />
))
and no more useObserver at it's childrens
A workaround I found was to destructure the array, now the changes are reactive since we are directly accessing the variables that are changing.
export default () => useObserver(() => {
const items = store.wishList.items.map(item => ({ ...item }))
return <WishListView items={items} />
})
and no more useObserver at it's childrens
It is actually better to mark all components as observer
if possible. For example, if you mark each Item
as observer and one of the items change its name then only this component will rerender. If you dont make Item
observer then your whole List
will rerender which is quite bad if have lots of items or deep DOM tree. Also it does not make sense to rerender whole list when just one item changes.
Look here for explanation https://mobx.js.org/refguide/observer-component.html#when-to-apply-observer
So your workaround is a bad pratice and should be used only as last resort if you dont have control over children components and can't make them observer
.