Mobx学习笔记
1. mobx的作用
官网描述
React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。
MobX提供机制来存储和更新应用状态供 React 使用
简单点: React -> 把数据映射到页面上进行渲染
Mobx -> 提供存储和更新状态的方法(状态管理)
2. mobx-react 装饰器环境搭建
- 创建一个react应用并添加mobx-react
create-react-app mobx
cd ./mobx
yarn install mobx-react mobx @babel/plugin-proposal-decorators-S
Notes:支持装饰器的环境搭建办法
-
本地不支持装饰器的话需要安装@babel/plugin-proposal-decorators, @babel/plugin-proposal-decorators主要是用了来支持装饰器的
-
接下来配置node_modules/babel-preset-create-app/create.js中的overrides为
overrides: [
isFlowEnabled && {
exclude: /\.tsx?$/,p
plugins: [require("@babel/plugin-transform-flow-strip-types").default]
},
isTypeScriptEnabled && {
test: /\.(tsx|js|jsx)?$/,
plugins: [
[
require("@babel/plugin-proposal-decorators").default,
{ legacy: true }
]
]
}
- 重启系统即可
3.mobx的基本使用方法
mobx 几个API介绍
- observable
- action
- computed
observable
observable将一个变量变为可以观测的变量, 被observable元素装饰后,该元素会变为可以观测的,当该元素改变时, 在页面上渲染的地方得到改变,但是通过observable中的元素,是readonly的,不能直接通过赋值来改变
使用方法:
class Store {
@observable counter = 0;
}
action
如果想要改变observable变量的值,就需要通过一个action定义的方法来对其值进行改变(也可以异步)
使用方法:
class Store {
// 略一些业务代码...
@action
IncreaseCounter() {
this.counter++;
}
}
computed
在Store中某些值是需要通过Store中的部分值通过计算而来,而computed可以在其依赖值变化的时候相应的发生变化
使用方法:
class Store {
// 略一些业务代码...
@computed
get getDetailTimer() {
return `${parseInt(this.counter / 3600)}Hour-${parseInt(this.counter / 3600 % 60)Min}`
}
}
4.mobx-react基本使用方法
- Provider
- observer
- inject
1.Provider
Provider和React, Context中的Provider一个道理,将某一个值可以注入到组件树的深层中,一般会直接在App.js的外部加入Provider并将Store注入整个app中
2. observer
observer将整个组件变为可以观察的,Observer干的事情:一个收集器,将所有在Observer观察组件下的observable变量收集起来,然后当Store中有所监控到的obsevable变量发生变化的时候,就会重新执行render函数!!!
因此通过observer来使得组件进行动态渲染
使用方法:
@observer
class Store extends Component {
// 略一些业务代码...
}
3.inject
inject通过将刚才provider提供的Store注入到特定的组件之中,注入之后就可以通过this.props.StoreName,StoreName为注入时的Store名
**一个坑:**要注意, inject和observer同时存在的时候,要先通过inject注入,再用observer进行监控
使用方法:
@inject('Store')
@observer
class Store extends Component {
// 略一些业务代码...
render() {
const { Store } = this.props; // 获取注入的Store
}
}
使用mobx的方法:
- 通过mobx构建公共的Store, 通过observable和action定义Store中的变量和方法(mobx)
- 在特定的地方生成Store实例, 并通过Provider来注入到组件树中 (mobx-react)
- 通过inject将Store注入到特定的组件中,并在组件中通过Store.param进行查询和通过Store.func实现状态的改变(mobx-react)
4. TodoList例子
- mobx在react中的角色
- mobx
- mobx-react
- typescript在mobx中的使用方法
具体的代码已在github上: React-todoList-typescript
0. mobx在react中的角色
- mobx: 用于创建Store
- mobx-react: 用于Store的注入和状态检测(将mobx连到react上)
1. mobx
// Store.ts
import { observable, action, computed } from "mobx";
import { appStoreState, todoState, todoItem } from "../Interface/Interface";
// appStoreState为AppStore的接口类
class AppStore implements appStoreState {
// 监测的todoItem
@observable todoItem = [{ id: 1, item: "Learn React", isDone: false }];
// 用于增加todo条目
@action
addTodoItem(event: todoItem) {
this.todoItem.push({
...event,
id: this.id
});
}
// 自动生成todoItem的id
@computed get id() {
return this.todoItem.length + 1;
}
// 自动计算未完成的项目数
@computed get unsolvedNumber() {
return this.todoItem.filter(elem => !elem.isDone).length;
}
// 删除一条错误数据
@action
deleteTodo(event: todoState) {
this.todoItem.splice(
this.todoItem.findIndex(elem => elem.id === event.id),
1
);
}
// 更改todo条目的状态
@action
changeEventStatus(event: todoState) {
let target = this.todoItem.find(elem => elem.id === event.id);
if (target) {
target.isDone = !target.isDone;
}
}
}
export { AppStore };
2. mobx-react
// App.js
import React from "react";
import "./App.css";
import { Provider } from "mobx-react";
import { AppStore } from "./Store/Store";
import Todo from "./component/Todo";
import Controller from "./component/Controller";
// 创建一个mobx的Store实例
const appStore = new AppStore();
const App: React.FC = () => {
return (
// 通过Provider注入Store
<Provider Store={appStore}>
<div className="App">
<Controller />
<Todo />
</div>
</Provider>
);
};
export default App;
通过mobx-react中的Provider来注入Store
// Todo.tsx
import { observer, inject } from "mobx-react";
import * as React from "react";
import { appStoreState, todoState } from "../Interface/Interface";
import "../scss/todo.scss";
// App的接口类
export interface IAppProps {
Store?: appStoreState;
}
// 通过inject注入Store,这里注入的Store就是之前Provider传入的Store
// 通过observer来检测
@inject("Store")
@observer
export default class App extends React.Component<IAppProps> {
changeStatus(todoItem: todoState) {
const { Store } = this.props;
if (Store) {
Store.changeEventStatus(todoItem);
}
}
removeItem(todoItem: todoState) {
const { Store } = this.props;
if (Store) {
Store.deleteTodo(todoItem);
}
}
renderTodoList = () => {
const { Store } = this.props;
if (Store) {
console.log(Store.todoItem);
return Store.todoItem.map((elem, index) => (
<div className="list" key={`${index}`}>
<div className="list-item">
<input
type="checkbox"
id={`checkbox-${index}`}
checked={elem.isDone}
className="list-item__checkbox"
/>
<span className="list-item__text">
<span>{`${elem.id}-${elem.item}`}</span>
</span>
</div>
<div className="list-control">
<button onClick={this.removeItem.bind(this, elem)}>Delete</button>
<button onClick={this.changeStatus.bind(this, elem)}>
{elem.isDone ? "Cancel" : "Completed"}
</button>
</div>
</div>
));
}
};
public render() {
const { Store } = this.props;
return (
<>
{this.renderTodoList()}
{Store && (
<div className="total">
Unsolved Numbers: <span>{Store.unsolvedNumber}</span>
</div>
)}
</>
);
}
}
具体的使用方法已经大致整理在上面了~