mobxjs / mobx-react

React bindings for MobX
https://mobx.js.org/react-integration.html
MIT License
4.85k stars 349 forks source link

observerable questions #851

Closed bugzpodder closed 4 years ago

bugzpodder commented 4 years ago

I came from redux and had a few questions that I couldn't find anywhere in the docs, so I just posted as an issue. I mainly looked at the newer way (useLocalStore/useObservable + context) rather than the older approach with @observable but my understanding that they are the same.

Store location: There are several ways of putting a store.

For observables: There are three ways and docs says they are almost identical. I can see observer component allows local rendering, so just going to put that aside. For useObservable hook, is it a real hook? (I think so) so it can't be used in conditional rendering right? ie if (loading) return <Spinner />, but otherwise this is slightly more performant than observe HOC because other component hooks won't cause rerender?

assuming a component is wrapped using useObservable or observable HOC:

  1. is it smart enough to track and rerender only if the store properties that component used were changed instead of any change from any mobx store
  2. What if a child component didn't useObservable but accessed store properties? It wouldn't subscribe to updates even though parent is? ie subscription only works in that component?
  3. forgetting to wrap observers or destructuring stores and thus not getting updates seems error prone esp for new users, any way to catch it?

other qs:

  1. are computed value in store memoized?
  2. how to do async actions in useLocalStore?
danielkcz commented 4 years ago

Sorry for the delay with the answer.

For your concerns about how to structure stores, I would say, there is no particular "right" way. It's even out of the scope of MobX. It's important to realize that the MobX role is to provide reactivity which is done by setting up observables along with some reactions (provided by (use)observer). How you structure your stores and data is completely up to you. I know that's a whole different approach you might know from Redux which is highly opinionated on that.

Either way, to provide some structure the projects like mobx-state-tree or mobx-keystone were created as an abstraction. If you need something more complex, don't be shy to grab one of those.

The useObserver is a real hook by definition because it has useState inside to cause a re-render on observable changes. You cannot call it conditionally.

  1. is it smart enough to track and rerender only if the store properties that component used were changed instead of any change from any mobx store

That's kinda a core mission of the MobX to track only values that are used. So yea, it is "smart enough" ;)

2. What if a child component didn't useObservable but accessed store properties? It wouldn't subscribe to updates even though parent is? ie subscription only works in that component?

That is correct, without some observer (reaction), the component won't re-render itself. The external reasons for render still apply. It's either from props change (can be optimized with React.memo) or context value change.

3. forgetting to wrap observers or destructuring stores and thus not getting updates seems error prone esp for new users, any way to catch it?

Some people just use observers for every component to avoid this pitfall. If you think it's problematic for performance, you should measure it first. It effectively boils down to creating a few objects in memory that won't be used. That means memory consumption could be higher with such pattern, but it's rarely a problem.

Destructuring stores is not a complicated problem, especially if you stick an observer everywhere. If you don't want that for whatever reasons then you need to be mindful when you destructure.

  1. are computed value in store memoized?

Yes and no. It's a somewhat complicated matter and generally, unless you need to hunt a performance, don't worry about it. MobX is clever enough to decide this for you.

2. how to do async actions in useLocalStore?

There is nothing specific to MobX about it. Do your async in a component and call actions on the store to change value. Or grab MST and learn about its async actions.

mweststrate commented 4 years ago

In addition, use https://mobx.js.org/refguide/api.html#observablerequiresreaction-boolean (mobx.configure({ observableRequiresReaction: true }) is a great way to have MobX warn you if you forget any observer, or to warn you if you use a computed value in a way that it won't be cached

On Wed, Apr 22, 2020 at 9:07 PM Daniel K. notifications@github.com wrote:

Sorry for the delay with the answer.

For your concerns about how to structure stores, I would say, there is no particular "right" way. It's even out of the scope of MobX. It's important to realize that the MobX role is to provide reactivity which is done by setting up observables along with some reactions (provided by (use)observer). How you structure your stores and data is completely up to you. I know that's a whole different approach you might know from Redux which is highly opinionated on that.

Either way, to provide some structure the projects like mobx-state-tree or mobx-keystone were created as an abstraction. If you need something more complex, don't be shy to grab one of those.

The useObserver is a real hook by definition because it has useState inside to cause a re-render on observable changes. You cannot call it conditionally.

  1. is it smart enough to track and rerender only if the store properties that component used were changed instead of any change from any mobx store

That's kinda a core mission of the MobX to track only values that are used. So yea, it is "smart enough" ;)

  1. What if a child component didn't useObservable but accessed store properties? It wouldn't subscribe to updates even though parent is? ie subscription only works in that component?

That is correct, without some observer (reaction), the component won't re-render itself. Only render can then come either from props change (can be optimized with React.memo) or context value change.

  1. forgetting to wrap observers or destructuring stores and thus not getting updates seems error prone esp for new users, any way to catch it?

Some people just use observers for every component to avoid this pitfall. If you think it's problematic for performance, you should measure it first. It effectively boils down to creating a few objects in memory that won't be used. That means memory consumption could be higher with such pattern, but it's rarely a problem.

Destructuring stores is not a complicated problem, especially if you stick an observer everywhere. If you don't want that for whatever reasons then you need to be mindful when you destructure.

  1. are computed value in store memoized?

Yes and no. It's a somewhat complicated matter and generally, unless you need to hunt a performance, don't worry about it. MobX is clever enough to decide this for you.

  1. how to do async actions in useLocalStore?

There is nothing specific to MobX about it. Do your async in a component and call actions on the store to change value. Or grab MST and learn about its async actions https://mobx-state-tree.js.org/concepts/async-actions.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/mobxjs/mobx-react/issues/851#issuecomment-618011559, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAN4NBAHQXRN6NM3356BUZDRN5FA3ANCNFSM4LZNHRYQ .

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.