Search code examples
reactjstypescriptreact-hooksmobxmobx-react

MOBX and react integration


getting in valid hook error in a re write of a clients app... (upgrading code)

mobx 6.3.8 mobx react 7.2.1 and mobx-state-tree 5.0.5 React 17.0.1 RN 0.64.3

i feel like the error is here. i googled the code line for use stores and it led me to the deprecated site... i dont know where to find new handling in the https://mobx.js.org/react-integration.html site... what would this be called?

import { createContext, useContext } from "react"
import { RootStore } from "./root-store"


const RootStoreContext = createContext<RootStore>({} as RootStore)

const RootStoreProvider = RootStoreContext.Provider

// hook error here? // export const useStores = () => useContext(RootStoreContext);

error: Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

adding more context... rootstore file

import { Instance, SnapshotOut, types } from "mobx-state-tree"

import { creatMediaPlayerModel } from "../../models/media-player"
import { createUserModel } from "../../models/user"
import { createContentModel } from "../../models/content"


export const RootStoreModel = types.model("RootStore", {
mediaPlayerStore: creatMediaPlayerModel(),
userStore: createUserModel(),
contentStore: createContentModel(),
})


export type RootStore = Instance<typeof RootStoreModel>

export type RootStoreSnapshot = SnapshotOut<typeof RootStoreModel>

Solution

  • From that error message:

    Hooks can only be called inside of the body of a function component.

    You are not calling a hook from inside the body of a function component. So you are breaking the rules of hooks.

    According the rules of hooks you can only call a hook from the top level of a react function component. If you are not inside a functional component, then you cannot use a react hook*.

    From the docs:

    Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.

    That means you need to call useStores from within a functional component.

    Something like:

    function MyComponent() {
      const myStores = useStores()
    
      // render component with data from stores here
      return <>{myStore.myData}</>
    }
    

    * The exception, sort of, is a custom hook which can call other hooks. Your useStores here is a custom hook. So it's fine to call useContext from that custom hook.

    But that custom hook must obey the same usage rules as the built in hooks, so all hooks are called from the body of a function component, there is just function in between.