全局状态管理库,虽说在项目中不一定需要,但总的来说,还是要掌握一种库比较好,以备不时之需
在 React 生态中说到全局状态管理库,肯定第一反应就是说 redux,我也不例外;但是为什么没有继续写 redux,因为 redux 确实挺麻烦的,有点不符合个人的思考方式(个人见解,若 redux 也有什么好的写法,也请评论告诉我)
什么是 Mobx
当你想学习一个新的库,当然最好的方式是看文档:MobX 官网
进入官网,Mobx 第一时间告诉了你:State(状态) 、Actions(动作) 和 Derivations(派生) 这三个概念
- 定义 State 并使其可观察
- 使用 Action 更新 State
- 创建 Derivations 以便自动对 State 变化进行响应
总的来说,就是可以将 State 存在任何数据结构中,再(最好只)用 Action 来定义操作 State 的方法,最后是 State 并且又不需要进一步交互的东西就是 Derivations
State 和 Action 都挺好理解的,就是 Derivations 有点小迷惑,不急,下面就明白了:
Mobx 将 Derivations 分成两种:
- Computed value;总是可以通过纯函数从当前的可观测 State 中派生
- Reactions;当 State 改变时需要自动运行的副作用(命令式编程和响应式编程之间的桥梁)
你可能还不太明白他们能干啥,给出官网的两个例子吧:
// 通过 computed 对派生值进行建模
import { makeObservable, observable, computed } from "mobx";
class TodoList {
todos = [];
get unfinishedTodoCount() {
return this.todos.filter((todo) => !todo.finished).length;
}
constructor(todos) {
makeObservable(this, {
todos: observable,
unfinishedTodoCount: computed,
});
this.todos = todos;
}
}
这里可以看到 unfinishedTodoCount 是定义的 computed,表示它仅在需要时自动更新,也就是说这个对象或这个对象中的属性被修改时自动更新
官网上描述:如果有人关心其结果时才会更新
上面是关于 computed 的,接下来看关于 reaction 的
reaction 的解释是当 State 改变时需要自动运行的副作用,这不正好可以用在 UI 组件上么?所以官网说最常用的 reaction 形式是 UI 组件
import * as React from "react";
import { render } from "react-dom";
import { observer } from "mobx-react-lite";
const TodoListView = observer(({ todoList }) => (
<div>
<ul>
{todoList.todos.map((todo) => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
Tasks left: {todoList.unfinishedTodoCount}
</div>
));
const TodoView = observer(({ todo }) => (
<li>
<input
type="checkbox"
checked={todo.finished}
onClick={() => todo.toggle()}
/>
{todo.title}
</li>
));
const store = new TodoList([
new Todo("Get Coffee"),
new Todo("Write simpler code"),
]);
render(<TodoListView todoList={store} />, document.getElementById("root"));
还有更详细的可以看官网,接下来就开始写代码了
在 React 中使用 Mobx
Step 1、准备好 Mobx 环境
下载啥的就不说了,官网介绍的很清楚了
首先在 src 文件夹下创建一个目录 store,再在其中创建 CounterStore.ts,并编写代码:
import { makeAutoObservable } from "mobx";
class CounterStore {
count = 0;
info = "Hello,World";
constructor() {
makeAutoObservable(this);
}
// 让 count 加一
addCount() {
this.count++;
}
// 重置数据
reset() {
this.count = 0;
this.info = "Hello,World";
}
// 改变 info
changeInfo(str: string) {
this.info = str;
}
}
export default CounterStore;
可以看到我这里使用了 makeAutoObservable,解释一下这个
makeAutoObservable 自动把属性、对象、数组、Maps 和 Sets 转化成 observable,也就是说它可以自动推断:
推断规则(官网上的):
- 所有 自有 属性都成为 observable
- 所有 getters 都成为 computed
- 所有 setters 都成为 action
- 所有 prototype 中的 functions 都成为 autoAction
- 所有 prototype 中的 generator functions 都成为 flow。(需要注意,generators 函数在某些编译器配置中无法被检测到,如果 flow 没有正常运行,请务必明确地指定 flow 注解。)
- 在 overrides 参数中标记为 false 的成员将不会被添加注解。例如,将其用于像标识符这样的只读字段。
这样就结束了么?并没有,再次到 store 文件夹下新建一个 index.ts, 代码如下:
// index.ts
import React from "react";
import CounterStore from "./CounterStore";
export const storesContext = React.createContext({
counterStore: new CounterStore(),
});
export const useStores = () => React.useContext(storesContext);
现在代码结构如下:
这样后,如果其他地方需要用到 Mobx 中的数据,直接使用这里的 useStores 就 Ok 了
Step 2、在代码中使用
首先新建一个 Test 组件,内容如下:
import { observer } from "mobx-react-lite";
import { useStores } from "../store";
const Test = observer(() => {
const { counterStore } = useStores();
// 点击 +1 按钮
const handleCilck = () => {
counterStore.addCount();
};
// 触发 input 的 change 事件
const inputChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
counterStore.changeInfo(e.target.value);
};
return (
<div style={{ backgroundColor: "lightblue" }}>
<h4>组件 Test</h4>
<div>count为:{counterStore.count}</div>
<div>info为:{counterStore.info}</div>
<button onClick={handleCilck}>+1</button>
<div>
<input type="text" value={counterStore.info} onChange={inputChange} />
</div>
</div>
);
});
export default Test;
counterStore 中就包括了我们在 CounterStore.ts 中定义的一系列数据
再定义一个组件 OtherTest.tsx,之后好观察效果:
import { useStores } from "../store";
import { observer } from "mobx-react-lite";
const OtherTest = observer(() => {
const { counterStore } = useStores();
const resetClick = () => {
counterStore.reset();
};
return (
<div style={{ backgroundColor: "lightgreen" }}>
<h4>组件 OtherTest</h4>
<div>count为:{counterStore.count}</div>
<div>info为:{counterStore.info}</div>
<button onClick={resetClick}>重置</button>
</div>
);
});
export default OtherTest;
之后到集合到 App.tsx 中观察效果,效果如下:
Step 3、查看结果
一个简单的使用 Mobx 的 Demo 就出来了,看效果:
啧,这样 Mobx 的基本用法就可以了,相比之下还是比 Redux 简单一些
最后
Redux 毋庸置疑肯定是个优秀的库,但是 Mobx 也绝对不差,所以还是选择适合自己的就行
参考链接:
若有不当之处,欢迎评论指出