5

Almost all examples (even the official documentation) use mobx-react-light in combination with useContext() hook.

However, React, many articles and blog-posts advice to NOT USE useContext() for middle/high frequent updates. Isn't state something which can update very frequenty?

Should one use the package in combination with the hook or will performance issues occur?

mleister
  • 1,697
  • 2
  • 20
  • 46

1 Answers1

4

useContext() is only used to get your store value (reference) and this value is not updated frequently, usually you only set your stores once and don't touch it after. When you use actions you only change observable values of your store and not the store itself. So basically Context is only used to pass reference to the store down the tree, after that all the work performed by MobX only.

Example from MobX docs:

import {observer} from 'mobx-react-lite'
import {createContext, useContext} from "react"

const TimerContext = createContext<Timer>()

const TimerView = observer(() => {
    // Grab the timer from the context.
    const timer = useContext(TimerContext) // See the Timer definition above.
    return (
        <span>Seconds passed: {timer.secondsPassed}</span>
    )
})

ReactDOM.render(
    <TimerContext.Provider value={new Timer()}
        <TimerView />
    </TimerContext.Provider>,
    document.body
)

So you pass value of new Timer() to TimerContext.Provider once when you render your app and it will never be changed or updated after that. Even docs says:

Note that we don't recommend ever replacing the value of a Provider with a different one. Using MobX, there should be no need for that, since the observable that is shared can be updated itself.

However, if you don't have SSR or don't test your app, then you don't even need to use Context at all, you can just use global variables/singletons as your stores and it will work perfectly fine.

Example:

// Initialize timer somewhere
export const myTimer = new Timer()

// You can use directly in the same file or import somewhere else
import { myTimer } from './file-with-your-timer'

// No props, `myTimer` is directly consumed from the closure or from another file
const TimerView = observer(() => <span>Seconds passed: {myTimer.secondsPassed}</span>)

ReactDOM.render(<TimerView />, document.body)

Quote from docs:

Using observables directly works very well, but since this typically introduces module state, this pattern might complicate unit testing. Instead, we recommend using React Context instead.

More about best practices with React: https://mobx.js.org/react-integration.html

Danila
  • 15,606
  • 2
  • 35
  • 67
  • Wait I think I misunderstood something. Let's say my store contains some users and I'm gonna add one more user via a method. Wouldn't this cause the useContext() to run again and therefore trigger all places where I "import" the context? Why should my context usually not update? I think its common to mutate the state by calling some actions? Please clearify – mleister Oct 18 '20 at 14:13
  • Yes the value of the context will not change but the values inside the passed ProviderValue will change (e.g. in your example inside the new Timer() object) when callin actions, right? – mleister Oct 18 '20 at 14:15
  • Yep, you change observable values of your store and not the store itself. Context is only used to pass the store down the tree, after that all the work performed by MobX only (and that's why you can just use singletons) – Danila Oct 18 '20 at 14:44
  • Ah okay so I am just passing the store reference down? Thanks, please maybe add that to your question and I ll accept your answer :) – mleister Oct 18 '20 at 15:12
  • I already have it in my answer "useContext() is only used to get your store value (**reference**)". I'll update my answer a little bit though – Danila Oct 18 '20 at 15:28