In this article I want to show you common best practices for using React with mobx. I will present them as rules. So whenever you come to a specific problem, try to solve it while sticking to these rules.
This article requires that you have a basic understanding of stores in mobx. If not, read this first.
Need a quickstart? I created a starter project, that implements the recommended practices. https://github.com/danielbischoff/react-mobx-starter
The stores represent the ui state
Alway keep in mind that the stores represent your application’s ui state. That means, when you save your stores’ state to a file, close your program and relaunch it with the loaded state, you will have the same program and will see the same things, like you have seen before closing your program. Stores are not meant to be ‘local databases’. They also hold information about what button is visible, disabled, the current text of an input filed, etc..
Separate your rest calls from the stores
Do not call your rest interface from within your stores. This makes them hard to test. Instead put these rest calls into extra classes and pass these instances to each store using the store’s constructor. When you write test, you can easily fake these api calls and pass your fake api instance to each store.
Keep your business logic in stores
Don’t ever write business logic in your components. When you write your business logic in components, you have no chance to reuse it, your business logic gets spread over many components, what makes it hard to refactor or reuse the code. Write the business logic with methods in the stores and call these methods from your components.
Don’t create global store instances
Don’t ever create global store instances. You can not write any reasonable and reliable tests for your components. Instead use the Provider to inject your stores into your components props. Then in your tests you can easily mock these stores.
Only the store is allowed to change its properties
Never change a store’s property directly in a component. Only the store is allowed to change its own properties. Always call a method from the store, that changes the store’s property. Otherwise your applications state (stores = application state) is updated from everywhere and you are slowly loosing control. That makes it very hard to debug.
Always annotate each component with @ observer
Annotating each component with @ observer allows each component to update on store prop changes. Otherwise the parent component annotated with @ component needs to rerender, to update its child component. So less components need to be rerendered.
Use @ computed
Let’s say you want your button disabled when a user doesn’t have the admin role and the application is not in “admin mode”. A single property like isAdmin in one store is not enough for this. You will need a computed property in your store.
You probably don’t need react router
You probably don’t need react router. As I said before you want your stores to represent your application’s state. When you let react router handle part of your application state, you don’t let your stores represent the application state. So keep your current displayed view in a property in one of your stores. Then you have one component that just renders what the property says.
How to decouple state and UI (a.k.a. you don’t need componentWillMount)
Strategies for dealing with routing, data fetching, authentication and workflow testing without the UI layer
Try to favor controlled components over uncontrolled components
Always try to build controlled components. This makes testing components and the overall complexity of your components easy to handle.
I hope I could help you with these simple tips.
Ask questions or give feedback in the comments below. 🙂