This article outlines several solutions for react state management, and including the following.
Context Usageredux usage and simple implementationmobx introduceWhen develop a webpage in using React, we typically divide the page into several regions(these regions are nested, and a parent region may contain multiple child regions). each region corresponds to a component, and each component will have same state.
- State belonging only to this component is represented by
state- State between sibling components need to be extracted to the parent component, represented by
state, and passed down throughprops.- State between components at different levels: if extracted to the ancestor component's
state, passing downpropswould be very cumbersome,Reactoffice provides theContextsolution to solve cross component communication problems.
Context provides a way to pass data between component trees without having manually add props to each component.
When to use Context
The purpose of Context is to share data that is global to component trees, such as currently authenticated user, theme, or prefered language.
Considerations before using Context
The primary use case for Context is when the components at different levels need to access the same data. Using it with caution, as it can reduce component reusability.
If you just want to avoid passing some props layer by layer, component composition(component composition) is sometimes a better solution than context.
The offical documentation provides a very clear example of theme, but I'll paste the code here to explain the following content.
jsximport React from "react";
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
export default class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “yellow” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="yellow">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “yellow”。
static contextType = ThemeContext; // 固定写法,感觉像hack
render() {
return <button style={{backgroundColor: this.context}}>ThemedButton</button>;
}
}
// 使用context步骤
// 1. 创建 createContext
// 2. Provider接收value,以保证有传下去的数据
// 3. 接收 Consumer或者class.contextType
上述案例有个梗,
static contextType = ThemeContext;注入 this.context获取,这是react的固定写法
该方式并不适用Function组件。
Modify ThemedButton to function
jsxfunction ThemedButton() {
let bgColor = React.useContext(ThemeContext);
return <button style={{backgroundColor: bgColor}}>
ThemedButton
</button>;
}
User information and theme color are two commonly used global states in a project.
jsxconst ThemeContext = React.createContext('light');
const PersonContext = React.createContext('XXX');
function Ancestor() {
const [color, setColor] = React.useState('yellow');
const [person, setPerson] = React.useState({name: 'zs', age: 12})
return <ThemeContext.Provider value={color}>
<button onClick={() => setColor(color === 'yellow' ? 'blue' : 'yellow')}>修改颜色</button>
<button onClick={() => setPerson(Object.assign({}, person, {
name: person.name === 'zs' ? 'ls' : 'zs'
}))}>修改Person</button>
<PersonContext.Provider value={person}>
<Parent></Parent>
</PersonContext.Provider>
</ThemeContext.Provider>
}
function Parent() {
return <Child/>
}
class Child extends React.Component {
render() {
return (
<div>
<h3>MultiContextUse</h3>
<ThemeContext.Consumer>
{theme => (
<PersonContext.Consumer>
{user => <div style={{backgroundColor: theme}}>{user.name}</div>}
</PersonContext.Consumer>
)}
</ThemeContext.Consumer>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Ancestor/>);
multiple context usage through Context.Consumer tag to receive context, comforms to JSX syntax。
Precautions
- The value state needs to be promoted to parent node's data reason
In the offical React documentation, Context is categoried as advanced section, belonging to React's high-level API, but the offical remmendation is not to use Context in stable versions of apps.
However, this doesn't mean we don't need to pay attention to Context. In fact many excellent React components use Context to accomplish their own functionality.
<Provider /> in react-redux, provides a global store through Contextreact-router manages routing state and other aspects through Context.In react component development, using context efficiently can make your components powerful and flexible.
Functional component can import a context by using useContext.
redux is a state container in JavaScript applications. It ensures consistent application behavior and easy to test.

You may not need redux
Redux is tool responsibilty for organizing state, but you also need to consider whether it suits you situation. Don't just use redux because someone tells you to. Take some time to think carefully about the benefits and drawbacks of using Redux.
In the following scenario, introducing Redux is a wiser choice:
- You have a considerable amount of data that changes over time;
- You state needs a single, reliable source of data;
- You fell that putting all the state in the top-level component is no longer sufficient to meet you needs.
- The state of a component needs to be shared.
Redux is a state container for JavaScript applications, providing predictable state management. It ensures consistent program behavior and is easy to test.

Here, I've written a simple implementation of Redux and React-redux.
jsx// redux 完整使用
import React, {Component} from "react";
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
// connect帮组⼦组件与store链接,其实就是⾼阶组件,这⾥返回的是⼀个新的组件
export default connect(
// mapStateToProps Function (state, [ownProps])
// state => ({count: state}),
// !谨慎使⽤ownProps,如果它发⽣变化,mapStateToProps就会执⾏,⾥⾯的state会被重新计算,容易影响性能
(state, ownProps) => {
// console.log('mapStateToProps ownProps ', ownProps);
return {
count: state
}
},
// mapDispatchToProps Object/Function 如果不定义 默认把dispatch注⼊组件
// 如果是对象的话,原版的dispatch就没有被注⼊了
// {
// add: () => ({type: 'ADD'})
// },
// Function (dispatch, [ownProps])
// !谨慎使⽤ownProps,如果它发⽣变化,mapDispatchToProps就会执⾏,容易影响性能
(dispatch) => {
let res = {
add: () => ({type: "ADD"}),
minus: () => ({type: "MINUS"})
};
res = bindActionCreators(res, dispatch);
return {
dispatch,
...res
};
},
// mergeProps Function
// 如果指定了这个参数,`mapStateToProps()` 与 `mapDispatchToProps()` 的执⾏结果和组件⾃身的 `props` 将传⼊到这个回调函数中。
// (stateProps, dispatchProps, ownProps) => {
// console.log("mergeProps", stateProps, dispatchProps); //sy-log
// return {omg: "omg", ...stateProps, ...dispatchProps};
// }
)(class ReactReduxPage extends Component{
render() {
// console.log(this.props);
const {count, dispatch, add, minus} = this.props;
return <div>
<h3>ReactReduxPage</h3>
<p>{count}</p>
<button onClick={() => dispatch({type: "ADD"})}>
add use dispatch
</button>
<button onClick={add}>add</button>
<button onClick={minus}>minus</button>
</div>
}
});
jsximport React, {Component} from 'react';
const ValueContext = React.createContext();
export const connect = (
mapStateToProps = state => state,
mapDispatchToProps,
) => WrapperComponent => {
return class extends Component {
static contextType = ValueContext;
constructor(props) {
super(props);
this.state = {
props: {}
};
}
componentDidMount() {
this.update();
const {subscribe} = this.context;
subscribe(() => {
this.update();
})
}
update () {
const {getState, dispatch} = this.context;
const stateProps =
mapStateToProps(getState());
let dispatchProps;
if(typeof mapDispatchToProps === 'function') {
dispatchProps = mapDispatchToProps(dispatch, this.props)
} else if (typeof mapDispatchToProps === 'object') {
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
} else {
dispatchProps = {
dispatch,
}
}
this.setState({
props: {
...stateProps,
...dispatchProps,
}
})
}
render () {
return <WrapperComponent {...this.props} {...this.state.props}/>;
}
}
}
export class Provider extends Component {
render () {
return <ValueContext.Provider value={this.props.store}>
{this.props.children}
</ValueContext.Provider>;
}
}
function bindActionCreators(creators, dispatch) {
const obj = {}
for(let prop in creators) {
obj[prop] = bindActionCreator(creators[obj], dispatch)
}
return obj;
}
function bindActionCreator(creator, dispatch) {
return (...args) => dispatch(creator(args))
}
Office definition
Simple and scalable state managementSumarize: Both Redux and Mobx have their advantages and disadvantages. For general applications, Redux is sufficient, while MobX is used for complex and high-performance business scenarios.
Refference:Why I migrated from Redux to Mobx
npx create-react-app XXXcd XXX && npm i && npm ejectnpm i mobx mobx-reactpackage.json babel增加pluginsjson[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
App.jsjsximport React from 'react';
import { makeObservable, observable, action } from 'mobx';
import { Observer } from 'mobx-react';
class App extends React.Component {
constructor(props) {
super(props);
makeObservable(this);
}
@observable
count = 0;
@action
add = () => {
++this.count;
}
render() {
return <Observer>
{
() => <div>
<p>{this.count}</p>
<button onClick={this.add}>ADD</button>
</div>
}
</Observer>
}
}
export default App;
Complete case, click here
函数式的优势
面向对象的优势
| Redux | Mobx |
|---|---|
| 数据流程很自然,需要依据对象引用是否变化来控制更新粒度。 | 数据流流动不自然,只有用到的数据才会引发绑定,局部精确更新,但免去了粒度控制烦恼。 |
| 如果充分利用时间回溯的特征,可以增强业务的可预测性与错误定位能力 | 时间回溯能力较为复杂 |
| 时间回溯代价很高,因为每次都要更新引用,除非增加代码复杂度,或使用 immutable。 | 自始至终一份引用,不需要 immutable,也没有复制对象的额外开销。 |
| 时间回溯的另一个代价是 action 与 reducer 完全脱节,数据流过程需要自行脑补。原因是可回溯必然不能保证引用关系。 | 数据流动由函数调用一气呵成,便于调试。 |
| 引入中间件,其实主要为了解决异步带来的副作用,业务逻辑或多或少参杂着 magic。 | 业务开发不是脑力活,而是体力活,少一些 magic,多一些效率。 |
| 但是灵活利用中间件,可以通过约定完成许多复杂的工作。 | 由于没有 magic,所以没有中间件机制,没法通过 magic 加快工作效率(这里 magic 是指 action 分发到 reducer 的过程)。 |
| 对 typescript 支持困难。 | 完美支持 typescript。 |
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!