Introduction to Redux and Mobx

Written by guptagaruda | Published 2017/08/17
Tech Story Tags: javascript | redux | mobx | react | web-development

TLDRvia the TL;DR App

In my earlier post, I compared the performance and memory profiles of a benchmark application written in AngularJS, React/Redux and React/Mobx. It’s quite obvious from the metrics that React with Redux or Mobx gives significant performance gains compared to AngularJS. In this post, I will go over the core concepts, benefits and gotchas with both the libraries.

All the below code snippets are from the ticker dashboard application from my earlier post.

Redux

Redux flow diagram

Core Concepts

http://redux.js.org/docs/introduction/ThreePrinciples.html

Single source of truth (UI State Tree)

The state of your whole application is stored in an object tree within a single store.

Here is the UI state tree for the stock ticker dashboard application.

UI State tree

Actions

The only way to change the state is to emit an action, an object describing what happened.

Action creator is a function returning the action object. In the below example, each Action is represented with a ‘type’ and ‘payload’.

Action Creators

Reducers

To specify how the state tree is transformed by actions, you write pure reducers.

Reducers are pure functions that modify the state in immutable way (returns new objects instead of mutating the object). Reducers shouldn’t cause side effects like making external api calls, triggering route changes.

Reducers

If we design the action interfaces to have a common property (‘type’ in this example), we can leverage TypeScript’s discriminated unions to get type checking as well as the intellisense support in each case statement.

Connected and Pure (Presentational) UI Components

Usage with React · Redux_Technically you could write the container components by hand using store.subscribe() . We don't advise you to do this…_redux.js.org

React-Redux library provides a Higher order React component which automatically listens for store.subscribe(listener) events. When an action is dispatched to the store, Redux will notify the state changed event to all subscribers. All “React-Redux” connected components are subscribers to the Redux store and every connected component’s mapStateToProps method is executed with every state change. This method helps you to slice the UI state tree and pick the data needed for the specific component tree.

Pure components are normal React components. They read data from props to render and execute the callbacks sent via props. They are not aware of Redux or UI state tree.

Here is how the component hierarchy would look:

Connected Components And Pure Components

Here is an example of the connected component. TickerTile component renders stock ticker details (ticker, company name, price, sma, volume etc) and depends on the tickerData as a prop. The parent component just sends the tickerId (e.g. MSFT) as prop (termed as ownprops). The Higher Order connect component takes mapStateToProps mapper function which takes the tickerId and returns tickerData from UI state’s tickerHash and tickerId.

TickerTile

React-Redux Connected Component

Here is an example of the pure component:

Pure Component

Pros

  • Everything with Redux is very explicit without any magic.
  • React’s deterministic state changes and view renders are great for testability and easy to debug any issues.
  • Redux patterns force developers to think hard on the schema of the UI state tree.
  • Patterns will be consistent across the codebase (at the cost of verboseness).
  • UI would simply become the representation of the state tree. Changes to the State tree can be easily traced/monitored as the actions play out.
  • Great Documentation and excellent community support.

Few things to watch out for

Designing UI State tree

  • Make sure your UI state tree is normalized and try to keep it as flat as possible (instead of deeply nested structures). At times, deciding whether a property should go into the UI state or not can be confusing. Using local component state is fine as long as other parts of the application do not care about it.

https://twitter.com/dan_abramov/status/727278011591045122

Helpful links:

Connected Components (containers) mapStateToProps method of all connected components will be executed with every state changed event of the Redux store. Optimizing this method is one of the critical steps to get optimal performance in complex applications. If you need to execute expensive operations like deriving computed data from the normalized state tree, use Redux reselect library. It memoizes the result and skips recalculating unless input references change (there is a gotcha if you’re trying to reuse the selectors in multiple components).

If there are minimal connected components, props need to be propagated several levels down of the component hierarchy which is clearly not ideal in the large applications. Redux used to recommend connecting few components to the Redux store. Now, the recommendation is to use as many as you need.

In the stock ticker dashboard application from my previous post, In the updates test scenario, with 1500 tickerTile (connected) components in the view, Redux refreshed the price/volume changes within 6ms.

These two PRs (authored by Dan Abramov, creator of Redux) achieved substantial performance gains by connecting several components to Redux store and by optimizing mapStateToProps.

Helpful Link(s):

**Batch dispatch calls**Anytime Redux’s dispatch method is called with an action, Redux executes all reducers, updates the state and notifies all the subscribers synchronously. React-Redux will then trigger mapDispathToProps on all the connected components.

If you’re updating different sections of the state tree through multiple actions at the same time, try to batch them to trigger only one notification.

Helpful Links:https://github.com/markerikson/redux-ecosystem-links/blob/master/store.md#store-change-subscriptionshttps://github.com/reactjs/redux/issues/2108http://blog.isquaredsoftware.com/2017/01/idiomatic-redux-thoughts-on-thunks-sagas-abstraction-and-reusability/#multiple-dispatching

**Redux Libraries**Redux is a tiny state management library (with minimal API) which acts as a building block for the higher level constructs. You need to bring in a lot of libraries to put together any real world application. It might be overwhelming at first (esp. for folks coming from AngularJS) but most of these libraries are small and come with good documentation.

https://github.com/reactjs/reselecthttps://github.com/paularmstrong/normalizrhttps://github.com/omnidan/redux-ignorehttps://github.com/gaearon/redux-thunkhttps://github.com/evgenyrodionov/redux-loggerhttps://github.com/tshelburne/redux-batched-actionshttps://github.com/tappleby/redux-batched-subscribe

**Functional Programming**Redux uses several functional programming patterns (currying, higher order functions, composition etc.). The patterns and code might look strange for folks coming from object oriented design background. But Redux comes with a great documentation and there are a ton of great articles/videos out there to gain familiarity with the patterns.

Mobx

From, https://mobx.js.org/

Both React and MobX provide very optimal and unique solutions to common problems in application development. React provides mechanisms to optimally render UI by using a virtual DOM that reduces the number of costly DOM mutations. MobX provides mechanisms to optimally synchronize application state with your React components by using a reactive virtual dependency state graph that is only updated when strictly needed and is never stale.

Mobx flow diagram (source: https://mobx.js.org/)

Core Concepts

https://mobx.js.org/intro/concepts.html

Observable State

MobX adds observable capabilities to existing data structures like objects, arrays and class instances.

Observable State

Derivations

Anything that can be derived from the state without any further interaction is a derivation (user interface, computed values).

User interface derivation

OR

/* a function that observes the state */autorun(function() {    console.log("Total Tickers",        tickerDataModel.getAllTickers().length    );});

Actions

From Mobx’s concepts:

Actions are functions that modify state.

All Derivations are updated automatically and atomically when the state changes. As a result, it is never possible to observe intermediate values.

All Derivations are updated synchronously by default. This means that, for example, actions can safely inspect a computed value directly after altering the state.

Actions in Mobx

Pros

  • A lot less mental overhead (no need to normalize state, no single ui tree, no reducers, no optimizing mapStateToProps, no connected components).
  • Developer velocity and productivity. You can very quickly put together decent sized applications with Mobx and the code also scales well within the large teams.
  • Porting services/view models from AngularJS application would be straight forward.
  • Mobx with React is blazingly fast in scenarios cut out for it. During the updates test scenario (from my earlier post), Mobx refreshed price/volume changes with-in 2ms (with 1500 tickers on the page).

Few things to watch out for

  • Mobx automatically runs all derivations (updating views, updating computed properties) whenever the state changes. It internally manages the dependency state graph. Its implicit nature of updates feels like magic. This may become problematic in large applications where you might prefer more control. (just my opinion, I may be wrong).
  • Observable arrays inherit from Object instead of Array, so the external libraries (like lodash) won’t be able to detect Observable arrays correctly. You need to convert to regular Array by calling observable.toJS() or observable.slice().
  • Mobx had higher memory footprint than Redux in some of my test scenarios. This may not be a big deal for most of the applications but please test out your specific scenarios.
  • For promises, every callback handler needs to wrapped with @action and derivations will be triggered for every callback handler.
  • Mobx synchronously triggers derivations whenever an observable property is changed, this can cause performance bottlenecks. Enforcing strict mode right from the beginning helps with this issue.

Helpful links:https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254https://mobx.js.org/best/pitfalls.html

Thanks for reading.

P.S. Thanks to Shyam Arjarapu for reviewing this article.


Published by HackerNoon on 2017/08/17