Easy MobX and Redux Comparison
In this article/video we'll start with a simple React app made with create-react-app that uses component state (setState) as its state management. We'll then take it from component state to MobX, and then again from component state to Redux. During the process we'll see what is similar and different between the two approaches.
The code we are looking at is asynchronous, so we'll see how each library handles async code differently.
The repository lives at https://github.com/leighhalliday/easy-mobx-redux-comparison and contains 3 branches:
- master: The component state version
- mobx: The MobX version
- redux: The Redux version
Component State to MobX
In MobX our data lives in a Store, which has at its easiest, observable
properties and action
functions which can modify the store's values.
import { observable, action, runInAction } from "mobx";import axios from "axios";export default class GalleryStore {@observable term = "";@observable images = [];@observable status = "initial";@actionfetchImages = async term => {this.status = "searching";this.term = term;this.images = [];try {const response = await axios.get("https://api.unsplash.com/search/photos",{params: {client_id: "abcdef",query: term}});this.setImages(response.data.results);} catch (error) {runInAction(() => {this.status = "error";});}};@actionsetImages = images => {this.images = images;this.status = "done";};}
We gain the ability to inject
our store into our components and make them act as observer
's to the changes in the store, by wrapping a Provider
component around our top level component.
import { Provider } from "mobx-react";import GalleryStore from "./stores/GalleryStore";const galleryStore = new GalleryStore();ReactDOM.render(<Provider galleryStore={galleryStore}><App /></Provider>,document.getElementById("root"));
Inject MobX into React
Now we can inject the store into our component, which will pass our whole store as a prop which in this case is called galleryStore
(defined in the Provider).
import { inject, observer } from "mobx-react";@inject("galleryStore")@observerexport default class App extends React.Component {// render, componentDidMount, etc...}
Component State to Redux
In Redux everything begins by calling the createStore
function, passing a reducer
, the initial state, and then applyMiddleware(thunk)
, which in this case allows us to use the thunk
middleware to handle asynchronous code.
import { createStore, applyMiddleware } from "redux";import thunk from "redux-thunk";import reducer from "./reducer";export default createStore(reducer,{term: "",images: [],status: "initial"},applyMiddleware(thunk));
Our reducer
modifies the state, based on the data coming when action
s are dispatch
ed to it. We want to take a copy of our state, and then modify it based on the action's type:
export default (state = {}, action) => {switch (action.type) {case "BEGIN_SEARCH":return {...state,term: action.term,images: [],status: "searching"};case "DONE_SEARCH":return {...state,images: action.images,status: "done"};case "ERROR_SEARCH":return {...state,status: "error"};default:return state;}};
The reducer is called when an action creator
(function) dispatches an action
(an object). In our case, we had to return a function which receives dispatch
, because we are dealing with asynchronous code (handled by thunk
).
import axios from "axios";const fetchImages = term => {return async dispatch => {dispatch({type: "BEGIN_SEARCH",term});try {const response = await axios.get("https://api.unsplash.com/search/photos",{params: {client_id: "abcdefg",query: term}});dispatch({type: "DONE_SEARCH",images: response.data.results});} catch (error) {dispatch({type: "ERROR_SEARCH"});}};};export { fetchImages };
Connecting Redux to React
To connect our Redux store to React we need to use the connect
function. But before we can do that, we must first wrap our top level component in a Provider.
import { Provider } from "react-redux";import store from "./store";ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById("root"));
Connect wants 2 functions, typically called mapStateToProps
and mapDispatchToProps
. This allows us to pass state into our component, and action creators which can dispatch actions to the reducer which will modify the state.
const mapStateToProps = state => {return {term: state.term,images: state.images,status: state.status};};const mapDispatchToProps = dispatch => {return {fetchImages: term => {dispatch(fetchImages(term));}};};export default connect(mapStateToProps,mapDispatchToProps)(App);
Conclusion
I hope you enjoyed a comparison between MobX and Redux! Redux is the most popular state management tool out there, but MobX is what I enjoy most... maybe because in my heart of hearts I like the "style" of object-oriented programming? Either way they are both great libraries and it's good to know what their differences are!