Redux and Mobx are two most popular state management tools for React apps, representing different design philosophis as object-oriented programming and functional programming. Recently, a new context api has been proposed to provide a native global store solution and received a lot of discussions. Here’s a brief comparion of different methods. I have to say before hand, most things in software engineering are based on opinions and tradeoffs, not facts or hard truth.
Redux
Basic data flow:
1 | graph LR |
Action
Actions are just plain object that contains type
and payload
property, such as
1 | { |
Actions should be returned by action creaters.
Reducer
Reducers are just pure functions that take the previous related
state and action, then return the new state
Reducers can be combined together by using a function combineReducers
from redux
Store
Single source of truth. Store should be created at the root level,
1 | import { createStore } from 'redux' |
APIs
- createStore
- create the single store at root level
- Store
- combineReducers
- applyMiddleware
- useful for async ajax requests
- bindActionCreators
- the only use case for this is when you want to pass some action creators down to a component that isn’t aware of Redux
- compose
- composes functions from right to left
react-redux
Makes the Redux store available to the connect() calls in the component hierarchy below.
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
Connects a React component to a Redux store.
connect( mapStateToProps, mapDispatchToProps )(component)
If no parameters are provided, it injects just dispatch and doesn’t listen to store
params:
-
mapStateToProps(state, [ownProps]): stateProps
-
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
related libraries
-
- Redux Thunk middleware allows you to write action creators that return a function instead of an action.
- The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. Thus it’s very useful for async actions and promises.
-
- a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, simple to test, and better at handling failures.
Feature
- reacting to actions or events
- single store
- functional programming paradigm
- immutable
- pure
- explicit update logic
- plain JavaScript
- more boilerplate
- normalized state
- flat state
- examples:
- business apps
- event based systems
- game events involving a complex reaction
redux todolist example:
Summary
Redux is an opinionated library that reinforces immutable data and functional programming pattern. It provides more granular control of data flow and shines in testability, scaleability, maintainability. It will be suitable for a large team and complex apps because it has mature best practices and community support.
MobX
Data flow:
observable
Observable values can be JS primitives, references, plain objects, class instances, arrays and maps.
Usage:
- observable(value)
- @observable classProperty = value
computed && autorun
They are both reactively invoked expressions, but use @computed if you want to reactively produce a value that can be used by other observers and autorun if you don’t want to produce a new value but rather want to achieve an effect. For example imperative side effects like logging, making network requests etc.
example:
1 | import {observable, computed} from "mobx"; |
(@)observer
The react components that are wrapped by observer will react to the change of observables.
action
It has similar concepts as action in redux. Simply call mobx.useStrict(true)
when your application is initialized, and MobX will throw anytime you (accidentally) try to modify state without using an action.
Feature
- Reacting to state changes
- multiple stores
- object-oriented programming and reactive programming paradigms
- mutable
- impure
- implicit update logic
- “magic” JavaScript
- less boilerplate
- denormalized state
- nested state
- Examples
- Real-time systems, dashboards, etc
- Text editors, presentation software, etc.
- Not for event based
mobx todo example:
Summary
Mobx is more suitable for simpler application, rapid prototyping and a small team. It has short learning curve for those that are already familiar with object-oriented programming. It can also be used in large projects if explicit constraints are added, such as useStrict.
new context api
The new context api tries to provide a native global store for React apps and solves the problems that old context api has: shouldComponentUpdate blocks context changes and Shifts complexity to user space
Context in React provides a mechanism for a child component to access a value in an ancestor component.
React.createContext()
It is passed the initial value (and optionally a fancy opt-out function that uses a bitmask). This returns an object with a “provider” and a “consumer”
Provider
The provider acts as an event emitter. It keeps track of the most recent context value, and a list of subscribers to be notified whenever it changes.
Consumer
The consumer accesses the provider’s event emitter using the context API. (This usage is fine because the event emitter itself does not change). When the provider emits a change event, the consumer is notified and calls setState on itself to schedule a re-render.
Example:
1 | const ThemeContext = React.createContext('light') |
Summary
New context api provides a native way to manage global store in react apps and is perfect for dependency injection (theme, intl, buildConfig), but it lacks the power to control the data flow. Redux or Mobx may still be needed to handle complex async data process.
Resource
Preethi Kasireddy - MobX vs Redux: Comparing the Opposing Paradigms
Redux or MobX: An attempt to dissolve the Confusion
Replacing redux with the new React context API