Search code examples
reactjsmobxmobx-react

React Mobx ContextProvider


can you please explain to me why I need to wrap my app in the Store Provider when using mobx with React Context. What I mean:

// Create Counter Store
class CounterStore {
 constructor() {
  makeAutoObservable(this)
  this.num = 0
 }
 inc() { this.num++ }
}

// Create React Context with store
const StoreContext = React.createContext(new CounterStore())

// Create hook for usage context
const useStore = () => React.useContext(StoreContext)

// Create component
const Counter = observer(() => {
 const counterStore = useStore()
 return (
  <>
    <div>{counterStore.num}</div>
    <button onClick={() => counterStore.inc()}>
      inc num
    </button>
  </>
 )
})

// Create App component
const App = () => {
 return (
  <Counter />
 )
}

Ok everything is working fine, I have access to the store inside my counter component.

But still i didnt wrap my app into store provider like:

/// same code as above with little change
// Now create context with no value:
const StoreContext = React.createContext()

// And create Provider with store.
const StoreProvider = ({children}) => {
 return <StoreContext.Provider value={new CounterStore()}>
           {children}
        </StoreContext.Provider>
}
Then wrap App component with provider:

render(
    <StoreProvider>
        <App />
    </StoreProvider>,
    document.getElementById("root")
);

This code works too. But i cant understand why i need to create SroreProvider and wrap my App with it, if all works fine without it


Solution

  • Short answer: you don't necessarily need to wrap everything with Provider if you have default store value for your context. Everything will work just fine because MobX creates its own subscription mechanism for observer components which allows them to re-render. (also worth to read this docs to understand why you might want to have context if you are not using MobX)

    Even more so, you might not even need context in that case at all. You can just create singleton instance of the store and import it directly where you need it and don't bother with provider.

    But sometimes you might want to have have context with provider. For example for server side rendering, or for testing purposes. Like imagine your inc method makes some api call. But for tests you don't want to do that, and while testing you can just replace your store with different one:

    const TestStoreProvider = ({children}) => {
      // With provider you can different store there
      return <StoreContext.Provider value={new CounterStoreForTests()}>
               {children}
            </StoreContext.Provider>
    }
    

    In that cases you can treat Provider as Dependency Injection for components.