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
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
I would recommend you to have multiple stores, to avoid chaining of stores. As we do in our application:
class RootStore {
@observable somePropUsedInOtherStores = 'hello';
}
class AuthStore {
@observeble user = 'Viktor' ;
constructor(rootStore) {
this.rootStore = rootStore;
}
// this will reevaluate based on this.rootStore.somePropUsedInOtherStores cahnge
@computed get greeting() {
return `${this.rootStore.somePropUsedInOtherStores} ${this.user}`
}
}
const rootStore = new RootStore();
const stores = {
rootStore,
bankAccountStore: new BankAccountStore(rootStore),
authStore = new AuthStore(rootStore)
}
<Provider {...stores}>
<App />
</Provider>
In such a manner you can access exactly the store you need, as mostly one store covers one domain instance. Still, both sub-stores are able to communicate to rootStore
. Set its properties or call methods on it.
If you do not need a cross store communication - you may not need a rootStore
at all. Remove it and don't pass to other stores. Just keep 2 siblings stores
Answering your question on injecting not a whole store, you may benefit from mapperFunction
(like mapStateToProps
in redux) docs here
@inject(stores => ({
someProp: stores.rootStore.someProp
})
)
@observer
class User extends React.Component {
// no props.rootStore here, only props.someProp
}