Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
uneet7 opened this issue Mar 19, 2019 · 13 comments

Comments

@uneet7
Copy link

uneet7 commented Mar 19, 2019

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

@fi3ework
Copy link
Contributor

fi3ework commented Mar 20, 2019

@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)

@uneet7
Copy link
Author

uneet7 commented Mar 20, 2019

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

@fi3ework
Copy link
Contributor

fi3ework commented Mar 20, 2019

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.

@uneet7
Copy link
Author

uneet7 commented Mar 20, 2019

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

@fi3ework
Copy link
Contributor

fi3ework commented Mar 20, 2019

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`
  }
}

@uneet7
Copy link
Author

uneet7 commented Mar 20, 2019

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.

@uneet7 uneet7 changed the title Way of Creating multiple stores with mobx and injecting it into to a Component ? Way of Creating multiple stores with mobx and injecting it into to a Component ? Way of Communication between the stores? Mar 20, 2019
@fi3ework
Copy link
Contributor

fi3ework commented Mar 21, 2019

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

@uneet7
Copy link
Author

uneet7 commented Mar 21, 2019

yeah exactly!

@fi3ework
Copy link
Contributor

I think computed can do this.

@urugator
Copy link
Collaborator

urugator commented Mar 21, 2019

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

@uneet7
Copy link
Author

uneet7 commented Mar 22, 2019

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

@msreekm
Copy link

msreekm commented Jul 1, 2019

@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 .

@lock
Copy link

lock bot commented Aug 30, 2019

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.

@lock lock bot locked as resolved and limited conversation to collaborators Aug 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants