React 18中MobX的使用

5,523 阅读4分钟

MobX是一个基于响应式编程的状态管理库,React和MobX是一对强力组合,React提供机制把应用状态转为可渲染组件树并对其进行渲染,而MobX提供机制来存储和更新应用的状态供React使用 MobX背后的哲学很简单:任何源自应用状态的东西都应该自动地获得, 包括UI 数据序列化, 服务器通讯等等 flow.png

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自动地给类中的每个属性和方法标记上observaleaction
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值,因为使用了泛型推到类型,这里可以推导出你可以输入的值

image.png

并且可以根据输入的值推导出返回的Store的类型

image (1).png image (2).png