The motivation to write mobx-state-router came from the frustration of dealing with subtle bugs in my code resulting from fetching data in componentDidMount()
. Moreover, why should a component be responsible for fetching data in addition to rendering it? It feels like a violation of the Single Responsibility Principle. Looking for a better way, I came across this article by Michel Weststrate—How to decouple state and UI. Here's an excerpt from the article which clearly describes the root cause of my problems:
I discovered that most React applications are not driven by the state that is stored in stores; they are also driven by the logic in mounting components.
- Interpreting route changes is often done in components; especially when using react-router. The router constructs a component tree based on the current URL, which fires of componentWillMount handlers that will interpret parameters and update the state accordingly.
- Data fetching is often triggered by the fact that a component is about to be rendered, and kicked off by the componentWillMount lifecycle hook.
Michel then shows how to decouple state and UI, resulting in a robust architecture where components don't have to fetch data. They are purely a function of the application state stored in stores. Stores become more like a state machine, making it easy to follow the transitions of our application. mobx-state-router provides a first-class implementation of this idea.
My hope is that mobx-state-router will allow developers around the world to create more robust React applications with less headaches. If you like my work, please star the repo and refer it to your friends. Suggestions and PRs are welcome!
Before integrating mobx-state-router into your application, it's important to understand how it works. The section introduces you to the key concepts.
At the heart of it, mobx-state-router provides a RouterStore
that stores the RouterState
.
RouterState
consists of 3 properties:
routeName
: A string that defines the state of the router. For example, "department"
in the e-commerce Live Demo, Mobx Shop.params
: A set of key-value pairs that enhances the state. For example, {id: "electronics"}
may signify that we are in the "department" state, but specifically in the "electronics" department.queryParams
: A second set of key-value pairs to further enhance the state. For example, {q: "apple"}
may signify that we want to query for the string "apple".As you may have guessed, this structure facilitates the decomposition of a URL into separate parts. However, from the router's viewpoint, this is simply application state that can be manipulated without any concerns about who is manipulating it (whether it's UI components or some other logic).
Now that we understand RouterState
, let's move to the top-left of the diagram. The HistoryAdapter
, which is responsible for translating the URL in the browser address bar to the router state and vice-versa. It is essentially an "observer" of the address bar and the router state.
Moving to the top-right, the RouterView
watches the router state and instantiates the associated UI component. Finally, the UI components themselves can change the router state in reaction to user actions, such as keyboard entries and mouse clicks.
Now let's understand these concepts better by walking through two common scenarios. The first is when the user enters a URL in the browser address bar:
HistoryAdapter
notices the change and decomposes the URL into various parts.HistoryAdapter
stores the decomposed URL into the RouterState
.RouterView
observes the change in RouterState
.RouterView
looks up the view associated with the new router state and instantiates it.The second scenario describes what happens when the user interacts with a view:
RouterState
to the item
route with the id
parameter set to item E01
.RouterView
observes the change in RouterState
.RouterView
looks up the view associated with the item
state and instantiates the Item page.HistoryAdapter
notices the change in RouterState
and composes a URL from that state.HistoryAdapter
sets the composed URL in the browser address bar.Hopefully, these scenarios gave you a good understanding of how mobx-state-router works. It's time to get started with a real app!