Coder Social home page Coder Social logo

mobx-redux-showdown's Introduction

Redux and Mobx showdown

I've been using Redux for a project, and honestly, I don't like it. I decided to write a simple app in Redux and then write it again in MobX to compare the two approaches to state management.

The app loads a network resource, and displays it. Pretty simple stuff. To make this a bit more realistic, I also wanted to handle:

  • loading states
  • errors
  • duplicate network requests (only one request should happen)

Update

2020-01-31: It's not React, but I added a Svelte example since it sensibly includes it's own store. I'm not going to mix Svelte into all the commentary below, so have a look for yourselves. (It's pretty sweet!)

2018-11-26: I received a total rewrite of the redux app by markerikson using redux-starter-kit, and I've added it to the shootout. I'll need to update my comments below since it addresses many of the pain points with Redux.

There are now three React apps in the repo:

  • mobx-app: MobX
  • redux-app: Immutable.js, sagas, hand-written action creators
  • redux-starter-kit-app: thunks, automatic action creators, simplified createStore, redux logic is in a single file, and more

tldr; There are many ways to use Redux - choose wisely!

Number of files

Let's start with something simple and concrete. The number of files:

Redux 9

Redux Starter Kit 7

MobX 6

Why does Redux have more files than MobX? That's because of the Redux way of looking at the world and splitting things into actions, reducers, selectors, and sagas (for handling the network request).

Redux

redux-app/src/redux/features/valueSets.js
redux-app/src/redux/reducers.js
redux-app/src/redux/sagas/sagas.js
redux-app/src/redux/selectors/valueSetSelectors.js

Redux Starter Kit

redux-starter-kit-app/src/features/valueSets.js
redux-starter-kit-app/src/store.js

MobX

mobx-app/src/ValueSetStore.js

With Redux, I initially had actions in a separate file, but that was a terrible idea in practice as it was just a lot of monkey-work to deal with imports. Following the ducks setup, I refactored so that actions, reducer, action creators and sagas are in one file. A definite improvement, but still, the selectors are in a separate file and things are a bit more spread out compared to MobX's single class.

redux-starter-kit-app uses some helpers to also reduce the valueSet store down to a single file. (I haven't fully digested the magic.) The additional file is just a global store that pulls in all the feature stores.

Verbosity

Ok, so Redux means you're managing more files, but what about the code in those files?

Let's compare how you would add a bit of data you add to your app:

Redux

  • a way to set the data (reducer)
  • a way to get the data (selector)
  • actions for getting and setting the data

MobX

  • add a variable to a store
  • make it observable

With Mobx and decorators, this can be done with one line. With Redux, the selectors and reducers are added in 2 different files (which we've covered) but there is also the additional idea of an "action". What's up with actions?

Since the parts of Redux are so loosely coupled, you cannot call functions or properties directly, but need to write actions. And then you have to write both sides of the action: the sender which dispatches the action, and a receiver to observe and handle the action.

Actions involve some brain-dead boilerplate like const FETCH_VALUESET = 'FETCH_VALUESET' which isn't hard, but is annoying. (And with copy/pasting it's easy to accidentally duplicate the string for two vars which will lead to some head scratching.)

Components

So you have a store, and now you want to access the data from a component:

Redux

  • connect your component
  • add/edit mapStateToProps to get (select) data
  • add/edit mapDispatchToProps to set (write) data

MobX

  • inject your store
  • call functions to get, set, etc.

With MobX, once you inject your store, you have access to every function and property of the store. You can call functions directly, and don't need to dispatch an action to trigger a network request, or call selectors to get the eventual result of the network request. And you don't need to create the mapStateToProps and mapDispatchToProps objects.

Creating a store

Let's take a step back and look at how you setup a store. This is something you don't have to do that often, but it's still critical.

Redux

import createSagaMiddleware from 'redux-saga'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './redux/reducers/reducers'
import rootSaga from './redux/sagas/sagas'

const sagaMiddleware = createSagaMiddleware()

const store = createStore(
  rootReducer, // 0: reducer
  composeWithDevTools(
    applyMiddleware(sagaMiddleware), // 1: middleware
  )
)

sagaMiddleware.run(rootSaga)

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        ...
      </Provider>
    )
  }
}

MobX

import { Provider } from 'mobx-react'
import ValueSetStore from './ValueSetStore'

const valueSetStore = new ValueSetStore()

class App extends Component {
  render() {
    return (
      <Provider ValueSetStore={valueSetStore}>
        ...
      </Provider>
    )
  }
}

Redux clearly requires much more hand-holding - mainly because it doesn't handle asynchronous actions out-of-the-box, you so need to configure it to use a separate library (redux-thunk or redux-saga). MobX is simple enough to be done from memory.

redux-starter-kit-app can create a store with redux-thunk middleware in one-line.

Debugging

In the Redux setup I added the Redux dev tools, because that seems to be a popular tool for visualizing how react works. It's pretty and makes a good first impression, but I prefer the raw JSON to the tree visual. And in practice, I found the devtools to be useful for solving easy problems (a missing property of an action), but unhelpful for solving trickier issues (actions being called unexpectedly or an action not being called at all). I find that adding a breakpoint and typing JSON.stringify(store) is often just as useful. It's also a bummer that breakpoints cannot be used with devtools, since once you pause on a breakpoint, the redux devtools are paused as well.

MobX has some in-browser debugging tools, but I've never found the need. You can just drop a breakpoint in your store, and then inspect or step through the code as you normally would.

Other Libraries

What about helpers and other libraries?

Redux

  • immutable
  • redux-saga

Redux Starter Kit

  • react-thunk

MobX

  • [none]

Immutable is not strictly needed, but was recommended for when your global store starts to get complicated. And in this example, just using getIn and setIn was simpler than having to dealing with plain javascript objects and the spread syntax.

As mentioned, Redux doesn't handle asynchronous operations out of the box, so you need to add some middleware. I looked at a bunch of different libraries, but the general consensus seemed to be that sagas were the new hotness and good for complex logic. They use generator functions and definitely have a learning curve.

Conclusion

Don't make a decision based on github stars alone.

Redux 52k

Svelte 29k

MobX 21k

Browse the code for the Svelte, Redux, Redux Starter Kit and MobX apps. Let me know if you have any issues or suggestions for improvement.

mobx-redux-showdown's People

Contributors

erjanmx avatar xinsight avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

mobx-redux-showdown's Issues

Some feedback

Interesting comparison :) Wanted to weigh in a bit.

A couple of comments first:

  • The links at the bottom of the Readme both link to the MobX version
  • It has always been recommended to co-locate selectors with the reducers (since only selectors and reducers should know the shape of the store), so that extra file seems a bit contrived

Other than that I would say that it is important to understand the motivations behind the different libraries. Redux is extremely useful (speaking from experience) when developing a large and complex applications where you want to trace all actions and updates to the state. This is now even better with the new and improved Redux dev-tools which let you trace where an action is being triggered from.
Due to the benefit this gives I don't mind the extra files personally. Also, it's not really something you do that often in my experience (dealing with the different files). Personally I try to limit the amount of actions (in particular avoiding "setter" actions and also keep in mind that several reducers can respond to the same action), so I spend more time in my React components building the actual app.

MobX is meant to be more "simple" I guess, while still being fast. Personally I avoid things that seem a bit too magical, MobX just reminds me a too much of Knockout, Angular, and Backbone+Stickit, which seemed great at first, but which gets frustrating as the app grows and you start to lose control.
Also worth noting that Sebastian Markbage has stated that, while MobX is probably great, it is not very much in line with the future of React (https://twitter.com/sebmarkbage/status/1032684851063705600).

So for a larger app that might grow more complex over time, Redux is atm a good choice imo. I know a lot of people are very happy with MobX as well, but I would be worried about long-term maintainability for larger projects, as well as compatibility with future versions of React.

For a simpler app I would probably just use basic setState in combination with the new Context API (which does not replace Redux...), or perhaps look into something like Unstated.

Hooks should also make it simpler to roll your own state management for smaller apps in the future.

This article gives a very good explanation, and the final thoughts are pretty much spot on in terms of how I feel about this personally:
https://www.robinwieruch.de/redux-mobx-confusion/#finalThoughts

Regards,

Einar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.