React 之 MobX 5-6
1.1、MobX
简单,可扩展的状态管理库
MobX 是由 Mendi, Coinbase, Facebook 开源和众多个人赞助商所赞助的
React 和 MobX 是一对强力组合,React 负责渲染应用的状态,MobX 负责管理应用状态供 React 使用
1.2、MobX 浏览器支持
MobX5 版本运行在任何支持 ES6 proxy 的浏览器,不支持 E11,Node.js6
MobX4 可以运行在任何支持 ES5 的浏览器上
MobX4 和 5 的 API 是相同的
2、开发前需要配置装饰器语法
2.1方式一: 启用装饰器语法支持
- 弹射项目底层配置:npm run eject
- 下载装饰器语法 babel:插件:
npm install @babel/plugin-proposal-decorators
- 在 package, json 文件中加入配置
"babel": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
2.2方式二: 启用装饰器语法支持
npm install react-app-rewired @babel/plugin-proposal-decorators customize-cra
- 在项目根目录下创建 config-overrides.js 并加入配置
const { override, addDecoratorsLegacy } = require("customize-cra");
module.exports = override(addDecoratorsLegacy());
- 更改 package.json 文件中的配置项
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
}
解决vscode编辑器关于装饰器语法的警告
“javascript.implicitProjectConfig.experimentalDecorators”: true
MobX 工作流
MobX 6
1. MobX 6 概述
-
MobX 是一个简单的可扩展的状态管理库,无样板代码风格简约。
-
目前最新版本为 6,版本 4 和版本 5 已不再支持。
-
在 MobX 6 中不推荐使用装饰器语法,因为它不是 ES 标准,并且标准化过程要花费很长时间,但是通过配置仍然可以启用装饰器语法。
-
MobX 可以运行在任何支持 ES5 的环境中,包含浏览器和 Node。
2. MobX 6 使用
2.1 下载
- mobx:MobX 核心库
- mobx-react-lite:仅支持函数组件(建议使用函数组件)
- mobx-react:既支持函数组件也支持类组件
yarn add mobx@6.1.8 mobx-react-lite@3.2.0
2.2 核心概念
- observable state:被 MobX 跟踪的状态。
- action:允许修改状态的方法,在严格模式下只有 action 方法被允许修改状态。
- computed:根据应用程序状态派生的新值,计算值。
2.3 工作流程
2.4一个简单例子
import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"
// 对应用状态进行建模。
class Timer {
secondsPassed = 0
constructor() {
makeAutoObservable(this)
}
increase() {
this.secondsPassed += 1
}
reset() {
this.secondsPassed = 0
}
}
const myTimer = new Timer()
// 构建一个使用 observable 状态的“用户界面”。
const TimerView = observer(({ timer }) => (
<button onClick={() => timer.reset()}>已过秒数:{timer.secondsPassed}</button>
))
ReactDOM.render(<TimerView timer={myTimer} />, document.body)
// 每秒更新一次‘已过秒数:X’中的文本。
setInterval(() => {
myTimer.increase()
}, 1000)
2.4.1 makeAutoObservable
// target: 将目标对象中的属性和方法设置为 observable state 和 action
// overrides: 覆盖默认设置, 将 target 对象中的某些属性或者方法设置为普通属性
// options: 配置对象, autoBind, 使 action 方法始终拥有正确的 this 指向
makeAutoObservable(target, overrides?, options?)
makeAutoObservable(this, {reset: false}, {autoBind: true})
2.4.2 总结
状态变化更新视图的必要条件
- 状态需要被标记为
observable state
- 更改状态的方法需要被标记为
action
方法 - 组件视图必须通过
observer
方法包裹
可以使用 makeAutoObservable
方法将对象属性设置为 observable state
,将对象方法设置为 action
方法
可以使用 observer
方法监控当前组件使用到的由 MobX 跟踪的 observable state
,当状态发生变化时通知 React 更新视图
3 常用API
3.1 makeObservable
import { action, makeObservable, observable } from "mobx"
class TodoListStore {
constructor() {
makeObservable(this, {
todos: observable, //数据观察
createTodo: action, //数据操作
totle: computed, //计算属性
....
})
}
}
3.2 computed
计算属性 当依赖状态发生变化后,计算值自动更新。
import { computed } from "mobx"
class TodoListStore {
constructor() {
makeObservable(this, {
unCompletedTodoCount: computed
})
}
get unCompletedTodoCount() {
return this.todos.filter(todo => !todo.completed).length
}
}
注意:计算值是被缓存的。
get unCompletedTodoCount() {
console.log("unCompletedTodoCount")
return this.todos.filter(todo => !todo.completed).length
}
{todoListStore.unCompletedTodoCount}
{todoListStore.unCompletedTodoCount}
{todoListStore.unCompletedTodoCount}
// 计算属性被调用多次, 但是方法内部的console.log 只会输出一次, 说明计算属性是被缓存的.
3.3 runInAction 方法
异步方法不可以作为action方法,需要使用 runInAction 处理
import {
runInAction
} from "mobx"
class TodoListStore {
async loadTodos() {
let todos = await axios
.get("http://localhost:3005/todos")
.then(response => response.data)
runInAction(() => {
todos.forEach(todo => {
this.todos.push(new TodoStore(todo.title))
})
})
}
}
3.4 autorun 方法
- 监控数据变化执行副作用,接收一个函数作为参数,参数函数用来执行副作用,当参数函数内部使用的 observable state, computed 发生变化时函数会运行,初始运行 autorun 方法时参数函数也会运行一次。
import { autorun } from "mobx"
import { useEffect } from "react"
function Counter() {
const { counterStore } = useRootStore()
useEffect(() => {
// 确保 autorun 方法只被初始化一次
autorun(() => {
console.log(counterStore.count)
})
}, [])
}
- 对于基本数据类型,属于值传递,mobx 只能跟踪到原始属性,跟踪不到复制后的值。
useEffect(() => {
let count = counterStore.count
autorun(() => {
// 错误写法, mobx 跟踪不到变量 count
console.log(count)
})
}, [])
- 对于引用数据类型,只要引用地址不发生变化,mobx 就可以进行跟踪
3.5 reaction 方法
监控状态变化执行副作用,接收两个函数作为参数,第一个函数返回要监控的状态,第二个函数用来执行副作用,只有当第一个函数返回的状态发生变化时,第二个函数才会执行。reaction 方法提供了更加细颗粒度的状态控制。
和 autorun 不同,reaction 初始时不会执行副作用。
import { reaction } from "mobx"
function Counter() {
useEffect(() => {
reaction(
() => counterStore.count,
(current, previous) => {
console.log(current)
console.log(previous)
}
)
}, [])
}