State of component

since mobx-react-lite@1.3.0

  1. useLocalStore<T, S>(initializer: () => T, source?: S): T

Local observable state can be introduced by using the useLocalStore hook, that runs its initializer function once to create an observable store and keeps it around for a lifetime of a component.

All properties of the returned object will be made observable automatically, getters will be turned into computed properties, and methods will be bound to the store and apply mobx transactions automatically. If new class instances are returned from the initializer, they will be kept as is.

Note that using a local store might conflict with future React features like concurrent rendering.

  1. import React from 'react'
  2. import { useLocalStore, useObserver } from 'mobx-react' // 6.x
  3. export const SmartTodo = () => {
  4. const todo = useLocalStore(() => ({
  5. title: 'Click to toggle',
  6. done: false,
  7. toggle() {
  8. todo.done = !todo.done
  9. },
  10. get emoji() {
  11. return todo.done ? '😜' : '🏃'
  12. },
  13. }))
  14. return useObserver(() => (
  15. <h3 onClick={todo.toggle}>
  16. {todo.title} {todo.emoji}
  17. </h3>
  18. ))
  19. }

What about global store?

The naming useLocalStore was chosen to indicate that store is created locally in the component. However, that doesn't mean you cannot pass such store around the component tree. In fact it's totally possible to tackle global state management with useLocalStore despite the naming. You can for example setup bunch of local stores, assemble them in one root object and pass it around the app with a help of the React Context.

Non observable dependencies

since mobx-react-lite@1.4.0 or mobx-react@6.0

It is important to realize that the store is created only once (per component instance)! It is not possible to specify dependencies to force re-creation, nor should you directly be referring to anything non-observable in the initializer function, as changes in those won't propagate.

The useLocalStore supports passing a second argument with a plain object of anything non-observable you want to be used in store derivations. It might be a value from props, useContext or even useReducer if you like to mix things up. The object you pass in the second arg should always have a same shape (no conditionals).

  1. import { observer, useLocalStore } from 'mobx-react' // 6.x
  2. export const Counter = observer(props => {
  3. const store = useLocalStore(
  4. // don't ever destructure source, it won't work
  5. source => ({
  6. count: props.initialCount,
  7. get multiplied() {
  8. // you shouldn't ever refer to props directly here, it won't see a change
  9. return source.multiplier * store.count
  10. },
  11. inc() {
  12. store.count += 1
  13. },
  14. }),
  15. props, // note props passed here
  16. )
  17. return (
  18. <>
  19. <button id="inc" onClick={store.inc}>
  20. {`Count: ${store.count}`}
  21. </button>
  22. <span>{store.multiplied}</span>
  23. </>
  24. )
  25. })

Note that internally the useAsObservableSource hook is used to wrap around passed object. If you don't need actions or computed properties, feel free to use that hook directly. See more about state outsourcing.