Correct way of Creating multiple stores with mobx and injecting it into to a Component - ReactJs

uneet7 picture uneet7 · Jan 27, 2019 · Viewed 14.4k times · Source

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. Also I prefer to construct children stores within master's store constructor. Moreover, I found that sometimes my child store has to observe some data from parent store, so I pass this 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 everytime to the component even if it needs a part of the root store? If not how to inject auth Store to the user component?

EDIT: I have made an answer concluding the github discussion. Link of the discussion provided in the answer

Answer

uneet7 picture uneet7 · Mar 28, 2019

This answer may be opinionated but it may help the community indirectly.
After a lot of research, I saw below approaches used in practice by many. General methods Have a root store that can act as a communication channel between stores.

Question 1: How to organise stores and inject them into the component?

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
}

Approach 1 and Approach 2 doesn't make any difference other than syntax difference. Okay! that is the injection part!

Question 2: How to have an inter-store communication? (Try to avoid it)

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. Use Computed. This approach is common for both the above approaches

AuthStore.js

export class AuthStore {    
    constructor(rootStore) {
        this.rootStore = rootStore
        @computed get dependentVariable() {
          return this.rootStore.userStore.changeableUserVariable;                                      
        }
    }
}

I hope this helps the community. For more detailed discussion you can refer to the issue raised by me on Github