Mobx: Way of Creating multiple stores with mobx and injecting it into to a Component ? Way of Communication between the stores?

Created on 19 Mar 2019  ยท  13Comments  ยท  Source: mobxjs/mobx

As suggested here in the Mobx documentation I have created multiple stores in the following manner:

class bankAccountStore {
  constructor(rootStore){
    this.rootStore = rootStore;
  }
...

class authStore {
  constructor(rootStore){
    this.rootStore = rootStore;
  }
...

And finally creating a root store in the following manner. As stated here I went on to construct an instance of "children" stores within "master" store constructor. Moreover, I found that sometimes my child store has to observe some data from parent store, so I pass this(of a parent) into child constructors

class RootStore {
  constructor() {
    this.bankAccountStore = new bankAccountStore(this);
    this.authStore = new authStore(this);
  }
}

Providing to the App in following manner:

<Provider rootStore={new RootStore()}>
  <App />
</Provider>

And injecting to the component in like this:

@inject('rootStore') 
@observer
class User extends React.Component{
  constructor(props) {
    super(props);
    //Accessing the individual store with the help of root store
    this.authStore = this.props.rootStore.authStore;
  }
}

Is it the correct and efficient way to inject the root store every time to the component even if it needs a part of the root store? If not how to inject auth Store or only required number of stores to the user component? How to share data between stores which can change in future?

I am sorry to ask it here as I didn't get much response here on stack overflow. I would like to know better approach as it may have an opinionated answer

MOBX Version - 5.9.0
MOBX-REACT Version - 5.4.3

โ” question

Most helpful comment

as @fi3ework suggests

export class AuthStore {    
    constructor(rootStore) {
      this.rootStore = rootStore        
    }

    @computed get dependentVariable() {
      return this.rootStore.userStore.changeableUserVariable;                                      
    }
}

scalable web apps in long run

  • keep state simple and minimal (use computed/render for anything that can be derived from another value)
  • keep state in form of tree (not graph)
  • keep the tree as flat as possible (normalize)
  • keep tree shape fixed (normalize)
  • share IDs instead of object references (normalize)
  • prefer coupling over indirection
  • prefer transparent and easy to understand state over reusable encapsulated state
  • prefer function composition over object composition
  • avoid artificial abstractions (is there actual benefit of having storeX,storeY?)
  • avoid reaction/autorun

All 13 comments

@Uneetpatel7 hi
You of course can can pass multi sub store to Provider like this:

const rootStore = new RootStore()

<Provider 
    rootStore={rootStore}
    bankAccountStore={rootStore.bankAccountStore}
    authStore={rootStore.authStore}
>
  <App />
</Provider>

to make it more clearer what store the component dependent on.

You can also pass more specific props to component by custom inject to make the component get better decoupling. (I like customizing inject so much)

Thanks for the reply. I can think of two approaches here.
Approach 1:

App.js

// Root Store Declaration
class RootStore {
    constructor() {
      this.userStore = new UserStore(this);
      this.authStore = new AuthStore(this);
    }
}    
const rootStore = new RootStore()

// Provide the store to the children
<Provider 
    rootStore={rootStore}
    userStore={rootStore.userStore}
    authStore={rootStore.authStore}
>
  <App />
</Provider>

Component.js

// Injecting into the component and using it as shown below
@inject('authStore', 'userStore)
@observer
class User extends React.Component {
    // only this.props.userStore.userVariable
}

Approach 2:

App.js

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

<Provider rootStore={rootStore}>
  <App />
</Provider>

Component.js

// Injecting into the component and using it as shown below
@inject(stores => ({
    userStore: stores.userStore,
    authStore: stores.authStore,
    })
)
@observer
class User extends React.Component {
    // no this.props.rootStore.userStore,userVariable here, 
    // only this.props.userStore.userVariable
}

Okay that is the injection part! Now I know a good design keeps stores independent and less coupled. But somehow consider a scenario where I want the variable in UserStore to change if a certain variable in AuthStore is changed. So I will approach it as discussed in this issue. This approach is common for both the above approaches

AuthStore.js

export class AuthStore {
    @observable dependentVariable = false;

    constructor(rootStore) {
        this.rootStore = rootStore
        autorun(() => {
            // changes the dependentVariable of this store 
            //  if changeableUserVariable of userStore changes
            this.dependentVariable = this.rootStore.userStore.changeableUserVariable;
        });
    }
}

Does approach 1 and approach 2 make any difference other than syntax difference ? Do these approaches works for the scalable web apps in long run? I would like to hear from you guys. Please @fi3ework @mweststrate @naivefun @urugator

hi,
I haven't deep into mobx-react's source code but in my practice it's same other than syntax difference and it works as my expect. How you pass store into component it's not important cause the observable target it's the property with @observable. It's same no matte how you reach them. You can choose a approach as you like.
For me, I prefer to specific in a more detail way, such as:

const InjectUser = inject(stores => ({
    userName: stores.userStore.userName,
    token: stores.authStore.token,
    })
)(User)

class User extends React.Component {
    // this.props.userName here, 
    // this.props.token here
}

So that User could be decoupling from store or MobX, it could be a stateless function with clear props.

@fi3ework What about the last aprroach governing the communication between the stores? Any comments

I usually define an action and communicate via rootStore, like this:

class AuthStore {
  constructor(rootStore){
    this.rootStore = rootStore;
  }

  @action
  addInfo(){
    this.rootStore.userStore.someObservableProp = `sth new`
  }
}

I want to listen to changes on observable variable of another store. I think you are tyring to change the observable variable of another store but that is not my question.

You mean a property in store A that observes a property in store B?

yeah exactly!

I think computed can do this.

as @fi3ework suggests

export class AuthStore {    
    constructor(rootStore) {
      this.rootStore = rootStore        
    }

    @computed get dependentVariable() {
      return this.rootStore.userStore.changeableUserVariable;                                      
    }
}

scalable web apps in long run

  • keep state simple and minimal (use computed/render for anything that can be derived from another value)
  • keep state in form of tree (not graph)
  • keep the tree as flat as possible (normalize)
  • keep tree shape fixed (normalize)
  • share IDs instead of object references (normalize)
  • prefer coupling over indirection
  • prefer transparent and easy to understand state over reusable encapsulated state
  • prefer function composition over object composition
  • avoid artificial abstractions (is there actual benefit of having storeX,storeY?)
  • avoid reaction/autorun

Thank you all for making the air clear. I hope this discussion may help to growing mobx community.

@urugator good points, let me also add one drawback with storing rootStore in each store is that when you have to deal with Json.serializer, store serializing will fail with circular reference error. This is critical in SSR .

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs or questions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

geohuz picture geohuz  ยท  3Comments

etinif picture etinif  ยท  3Comments

mdebbar picture mdebbar  ยท  3Comments

weitalu picture weitalu  ยท  3Comments

kmalakoff picture kmalakoff  ยท  4Comments