MobX是一个基于响应式编程的状态管理库,React和MobX是一对强力组合,React提供机制把应用状态转为可渲染组件树并对其进行渲染,而MobX提供机制来存储和更新应用的状态供React使用 MobX背后的哲学很简单:任何源自应用状态的东西都应该自动地获得, 包括UI 数据序列化, 服务器通讯等等
1.核心概念
MobX的核心概念有三个:State(状态)、Actions(动作)、Derivations(派生)
1.1.定义可观察的State
MobX通过observable标记一个可以被观察的状态并跟踪它们,只需要直接给它们赋值即可实现状态的修改
- 方法一:显示地标记observable和action
import { makeObservable, observable, action, computed } from "mobx";
export class Store {
count: number = 0;
price = 0;
amount = 1;
constructor() {
makeObservable(this, {
count: observable, // 标记observable
price: observable,
amount: observable,
add: action, // 标记action
});
}
add() {
this.count += 1;
}
}
- 方法二:通过
makeAutoObservale自动地给类中的每个属性和方法标记上observale和action
import { makeAutoObservable, observable, action, computed } from "mobx";
export class Store {
count: number = 0;
price = 0;
amount = 1;
constructor() {
makeAutoObservable(this);
}
add() {
this.count += 1;
}
}
1.2.使用Action更新State
Action可以理解为任何可以改变State的代码,比如用户事件处理,后端推送数据处理等等 在上面的例子中,add方法改变了count的属性值,而count是被标记为observale的,MobX推荐我们将所有修改observale的值的代码标记为action
1.3.创建Derivations以便自动对State变化进行响应
任何来源是State且不需要进一步交互的东西都是Derivations
MobX区分了两种Derivations:
- Computed:计算属性,可以用纯函数的形式从当前可观测的
State中派生 - Reactions:当State改变时需要运行的副作用
注:副作用可以看成是响应式编程和命令式编程之间的桥梁
- 通过computed对派生值进行建模
import { makeAutoObservable } from "mobx";
export class Store {
count: number = 0;
price = 0;
amount = 1;
constructor() {
makeAutoObservable(this);
}
add() {
this.count += 1;
}
get total() {
console.log("computed render");
return this.price + this.amount;
}
// computed可以有setter方法
set total(value: number) {
this.price = value;
}
}
2.MobX配合React Hooks创建全局状态管理
依赖包版本信息:React^18.0.0 React-DOM^18.0.0 MobX^6.5.0 MobX-React^7.3.0
- 安装MobX、MobX-React
pnpm add mobx mobx-react
2.1.创建Store
使用TypeScript的Class语法创建两个Store,在构造器中使用makeAutoObservable()来给类中的属性和方法自动标记状态
// /store/store.ts
import { makeAutoObservable } from "mobx";
export class Store {
count: number = 0; // 这些属性会被自动标记为observable
price: number = 0;
amount: number = 1;
constructor() {
makeAutoObservable(this);
}
// 改变observable的方法,会被自动标记为action
add() {
this.count += 1;
}
// 使用get set的方法,会被自动标记为computed
get total() {
console.log("computed render");
return this.price + this.amount;
}
set total(value: number) {
this.price = value;
}
}
// /store/user.ts
import {makeAutoObservable} from 'mobx';
export class User {
user: string = 'jiacheng_huang';
constructor() {
makeAutoObservable(this);
}
set(name: string) {
this.user = name;
}
}
- 创建一个Store层的根目录,来实例化上面创建好的类
// /store/index.ts
import {Store} from './Store';
import { User } from './User';
/** 将每个Store实例化 */
export const RootStore = {
store: new Store(),
user: new User()
}
2.2.在项目入口使用Context共享全局的Store对象
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import Routers from "./routers";
import { Provider as MobxProvider } from "mobx-react";
import { RootStore } from "./store";
function App() {
return (
/* 使用MobX-React提供的Provider写入Store Context */
<MobxProvider {...RootStore}>
<Routers />
</MobxProvider>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
到此为止,MobX创建和全局挂载的步骤已经完成了,要在组件内的任何地方使用,我们只需要编写一个useStore的公用Hooks来取值即可
2.3.编写一个获取Store的公用Hooks
因为上面使用了makeAutoObservableAPI来自动地标记类中的属性和方法,所以这里可以使用mobx-react提供的MobXProviderContext来获取已经创建好的Context,再配合React的useContext,即可实现状态的获取 如果你使用的也是TypeScript,可以参考我下面的泛型写法,来实现这个Hooks参数的自动获取和返回值的自动推到
// /hooks/useStore.tsx
import { MobXProviderContext } from 'mobx-react';
import { useContext } from 'react'
import { RootStore } from '../store'
// 根据RootStore来实现参数的自动获取和返回值的自动推导
function useStore<T extends typeof RootStore, V extends keyof T>(name: V): T[V] {
const store = useContext(MobXProviderContext) as T;
return store[name]
}
export default useStore;
2.4在任意组件中使用
MobX要在组件中使用,首先要用observer包裹,将其变为响应式组件,然后使用我们刚才创建好的Hooks来获取指定的Store即可
import { observer } from "mobx-react";
import useStore from "../hooks/useStore";
export default observer(() => {
const store = useStore("store");
return (
<div>
{store.total}
<button onClick={() => (store.total = 3)}>Click</button>
</div>
);
});
这里调用useStore时,要输入的就是你RootStore中定义的key值,因为使用了泛型推到类型,这里可以推导出你可以输入的值
并且可以根据输入的值推导出返回的Store的类型