Search code examples
javascriptreactjstypescriptmobx

How to combine multiple stores as Root Store in Mobx and get access of in each other's fields and functions


I don't know how to combine two stores in one RootStore (using Typescript). For example, I have liveImageStore.ts (with methods which request image, also holds array of last 10 image's urls) and notificationStore.ts (there are some logic to set/clear notification for further using throughout in whole application. And request in liveImageStore.ts provide some errors (ex., in case of http connection troubles). I would like to call function from notificationStore.ts (setNotification which push in store new notification) in request method from first store. But how to forward and type it all? Notifier component show message use notificationStore.


Solution

  • This is how I'm using the root store pattern:

    class RootStore {
      childStoreOne: ChildStoreOne
      childStoreTwo: ChildStoreTwo
    
      constructor() {
        this.childStoreOne = new ChildStoreOne(this)
        this.childStoreTwo = new ChildStoreTwo(this)
      }
    }
    
    class ChildStoreOne {
      root: RootStore
      constructor(root: RootStore) {
        this.root = root
      }
      methodOne() {}
    }
    
    class ChildStoreTwo {
      root: RootStore
      constructor(root: RootStore) {
        this.root = root
      }
    
      getSomethingFromStoreOne() {
        this.root.childStoreOne.methodOne()
      }
    }
    

    And this is the React part:

    // holds a reference to the store (singleton)
    let store: RootStore
    
    // create the context
    const StoreContext = createContext<RootStore | undefined>(undefined);
    
    // create the provider component
    function RootStoreProvider({ children }: { children: ReactNode }) {
      //only create the store once ( store is a singleton)
      const root = store ?? new RootStore()
      store = root
      return <StoreContext.Provider value={root}>{children}</StoreContext.Provider>
    }
    
    // create the hook
    function useRootStore() {
      const context = useContext(StoreContext)
      if (context === undefined) {
        throw new Error("useRootStore must be used within RootStoreProvider")
      }
    
      return context
    }
    

    Just make sure you don't access other stores in the constructor functions, because of the initialization order some child store might not be created at that point.

    I've written a post about this pattern a while ago: Mobx root store pattern with react hooks you will also find a link to a demo project with Nextjs.