mobxjs / mobx-react-lite

Lightweight React bindings for MobX based on React 16.8 and Hooks
https://mobx.js.org/react-integration.html
MIT License
2.13k stars 90 forks source link

Inter store communication in multiple global stores #301

Closed jashwant closed 4 years ago

jashwant commented 4 years ago

I am using multiple global stores pattern as described in docs.

How can I get access of one store inside another?

If we look into multiple global stores pattern example, how can I access CounterStore inside ThemeStore ?

Why I am trying to do?

When user successfully authenticates, I am trying to call setToken action of appStore from action of authStore.

What I tried?

I tried using useStores() as mentioned in recipe, but it throws error as it cannot be used outside react component.

I cannot directly use appStore, because as recommended in recipes, I am exporting class ( instead of exporting class instance )

kubk commented 4 years ago

Instead of this:

export const storesContext = React.createContext({
  counterStore: new CounterStore(),
  themeStore: new ThemeStore(),
})

Try this:

const counterStore = new CounterStore();

export const storesContext = React.createContext({
  counterStore: counterStore,
  themeStore: new ThemeStore(counterStore),
})

This is basically what Dependency Injection is about. As a bonus, your code becomes unit-testable, because you can mock CounterStore while testing ThemeStore

jashwant commented 4 years ago

Thanks @kubk for pointing me in right direction.

I needed more general usage, so I created an empty Root store which acts as a bridge among all stores.

I am not sure if it's a right way to do things, but it works quite well.

export class RootStore {}
export class AuthStore {
  constructor(rootStore) {
    this.rootStore = rootStore;
    rootStore.authStore = this;
  }
export class UserStore {
  constructor(rootStore) {
    this.rootStore = rootStore;
    rootStore.userStore = this;
  }
const rootStore = new RootStore();
const authStore = new AuthStore(rootStore);
const userStore = new UserStore(rootStore);

export const storesContext = React.createContext({ 
authStore,
userStore
});

And then inside AuthStore, I can access userStore like following:

export class AuthStore {
 @action callApi() {
   let username = this.rootStore.userStore.username;
 }
}

And I can still call do

const { authStore } = useStores();

inside React component.