一.什么是mobx
MobX类似于vue中的vuex,是一种较为简洁的状态管理解决方案,他使用类装饰器或函数装饰可观察值、动作和计算值,然后在我需要的任何地方使用组件中的存储,我们将只需将存储注入组件并在我需要的任何地方观察状态. 并且有效的使用它。
MobX 的一些核心原则是:
- MobX可以有多个存储来存储应用程序的状态。
- 任何可以从状态派生而不需要任何进一步交互的东西都是派生。
- Action是任何可以改变状态的代码。
- 当状态发生变化时,所有的派生都会自动和自动地更新。
二.mobx初始化
2.1 mobx依赖安装
yarn add mobx mobx-react -S
yarn add react-app-rewired customize-cra @babel/plugin-proposal-decorators -D
2.2 mobx依赖配置
在根目录下新建config-overrides.js
const { override, addDecoratorsLegacy } = require("customize-cra")
module.exports = override(
addDecoratorsLegacy()
)
将package.json的script修改为如下
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom" ,
"eject": "react-scripts eject"
},
三.基本使用
3.1 基础计数器案例
- 1.初始化容器仓库
- 2.在组件中使用 mobx容器状态
- 3.在组件中发起action修改容器状态
import React,{Component} from "react";
import ReactDOM from "react-dom";
import { observable, action, makeAutoObservable } from "mobx";
import {observer} from 'mobx-react'
//1.初始化容器仓库
class Store {
//将count转化为可以被观测的数据
@observable count = 0;
//mobx6.0以后一定要加这一行
constructor(){
makeAutoObservable(this)
}
@action
increment=()=> {
this.count++;
}
}
//2.在组件中使用 mobx容器状态
@observer
class App extends Component {
render(){
const {store}=this.props
return (
<>
<h1>I am ws</h1>
<h1>{store.count}</h1>
{/* 调用仓库里面的方法 */}
<button onClick={store.increment}>加一</button>
</>
)
}
}
//3.在组件中发起action修改容器状态
//这里将Store仓库传递给组件,连接mobx仓库与组件
ReactDOM.render(<App store={new Store()}/>, document.getElementById("root"));
3.2 装饰器语法
装饰器(Decorator)是es6之后一种与类(class)相关的语法,用来注释或修改类和类方法,装饰器只能用于类和类的方法,不能用于函数,因为函数存在函数提升。如果一定要装饰函数,可以采用高阶函数的形式直接执行。
3.2.1 装饰器的基本使用
//装饰器就是修饰类的一个函数
//添加类的静态成员
function fn(target){
target.foo='ws'
}
@fn
class Myclass{}
console.log(Myclass.foo);
//带参数的装饰器
@fn2(10)
class Mycfass{}
function fn2(value){
return function(target){
target.count=value
}
}
console.log(Mycfass.count);
);
3.2.2 装饰器实现继承
//这里将list对象展开后,将其属性放到目标元素的原型上
function mixin(...list){
return function(target){
Object.assign(target.prototype,...list)
}
}
const Ws={
play(){
console.log('ws');
}
}
@mixin(Foo)
class Myclass{
}
//这里继承了mixin的方法
new Myclass().play()
3.3.3 装饰器修饰类成员
//装饰器修饰类的内部成员
class Mrclass{
@readonly message='heelo'
@noenumerable bar='fff'
}
function readonly(target,name,descriptor){
console.log(target);//目标类的prototype
console.log(name);//被修饰类成员的名称
console.log(descriptor);//被修饰的类成员的描述对象
//只读的
descriptor.writable=false
}
function noenumerable(target,name,descriptor){
console.log(target);//目标类的prototype
console.log(name);//被修饰类成员的名称
console.log(descriptor);//被修饰的类成员的描述对象
//不可以被遍历
descriptor.enumerable=false
}
const c=new Mrclass()
console.log(c.message);
for(let k in c){
console.log(c[k]);
}
3.3 属性方法
3.3.1 observable
Observable将普通的数据转化为可以被观测的数据,它的值可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射,希望数据发生改变触发试图更新,就可以使用这个装饰器来修饰数据
class Store {
//将count转化为可以被观测的数据,外部可以检测它的改变
@observable count = 0
//一定要加这一行
constructor(){
makeAutoObservable(this)
}
@action
increment=()=>{
this.count++
}
foo='bar'
}
3.3.2 computed
计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。 概念上来说,它们与excel表格中的公式十分相似。 不要低估计算值,因为它们有助于使实际可修改的状态尽可能的小。 此外计算值还是高度优化过的,所以尽可能的多使用它们。
class Store {
@observable price=10
@observable num=5
@computed get totalPice(){
return this.price*this.num
}
}
<p>总价格:{store.totalPice}</p>
<p>总价格:{store.totalPice}</p>
<p>总价格:{store.totalPice}</p>
3.3.3 action
3.3.3.1基本使用
需要改变observable中的数据,最好使用action修改,尽可能避免使用store.的方式直接修改
import React from 'react';
import ReactDOM from 'react-dom';
import {observable,action,makeAutoObservable, autorun,computed} from 'mobx'
import {observer}from 'mobx-react'
//1.初始化mobx仓库
class Store {
@observable count = 0
//一定要加这一行
constructor(){
makeAutoObservable(this)
}
@action
increment=()=>{
this.count++
}
foo='bar'
@observable price=10
@observable num=5
@computed get totalPice(){
return this.price*this.num
}
@action change(){
this.count=10
this.foo='hello'
this.foo='word'
}
}
const store=new Store()
autorun(()=>{
//这里只触发了一次
console.log(store.foo,store.count,'fffrrrf');
})
store.count=20
store.foo='ttt'
store.change()
@observer
class App extends React.Component{
render(){
const {store}=this.props
return(
<div>
<h1>我是一个仓库</h1>
<h2>{store.count}</h2>
<button onClick={store.increment}>增加</button>
<p>总价格:{store.price*store.num}</p>
</div>
)
}
}
ReactDOM.render(
<App store={new Store()}/>,
document.getElementById('root')
);
3.3.4 异步action
import React from "react";
import ReactDOM from "react-dom";
import {
observable,
action,
makeAutoObservable,
autorun,
configure,
runInAction
} from "mobx";
import { observer } from "mobx-react";
configure({
enforceActions: "observed",
});
//初始化mobx仓库
class Store {
@observable count = 0;
@observable num1 = 0;
@observable num2 = 0;
//一定要加这一行
constructor() {
makeAutoObservable(this);
}
@action
increment = () => {
this.count++;
};
//1.定义一个同步函数用于修改,在异步函数中调用
@action.bound
changeCountAsync() {
setTimeout(() => {
this.shngecount()
}, 1000);
}
@action.bound
shngecount(){
this.count=100
}
//2.在异步函数中调用action函数,第一参数是修改函数的名称,这里使用函数自调用
@action.bound
changenum1Async() {
setTimeout(() => {
action('changnum1',()=>{
console.log(this);
this.num1=1002
})()
}, 1000);
}
//3.使用runInAction
@action.bound
changenum2Async() {
setTimeout(() => {
runInAction(()=>{
console.log(this);
this.num2=1003
})
}, 1000);
}
}
const store = new Store();;
autorun(() => {
//当store.count发生改变。他会自动触发,需要给数据成员加上@observable
console.log( store.count, "fffrrrf");
console.log( store.num1, "num1ffrrrf");
console.log( store.num2, "num2ffrrrf");
});
//函数调用
store.changeCountAsync()
store.changenum1Async()
store.changenum2Async()
@observer
class App extends React.Component {
render() {
const { store } = this.props;
return (
<div>
<h1>我是一个仓库</h1>
<h2>{store.count}</h2>
<button onClick={store.increment}>增加</button>
</div>
);
}
}
ReactDOM.render(<App store={new Store()} />, document.getElementById("root"));
- action.bound
绑定this,让当前this指向store,注意: action.bound 不要和箭头函数一起使用;箭头函数已经是绑定过的并且不能重新绑定。
3.3.5 configure
强制使用action修改数据,使用严格模式。
import {observable,action,makeAutoObservable, autorun,computed,configure} from 'mobx'
configure({
enforceActions:'observed'
})
3.3.6 runInAction
import React from 'react';
import ReactDOM from 'react-dom';
import {observable,action,makeAutoObservable, autorun,computed,configure,runInAction} from 'mobx'
import {observer}from 'mobx-react'
configure({
enforceActions:'observed'
})
//1.初始化mobx仓库
class Store {
@observable count = 0
@observable foo= 'sxsddde'
//一定要加这一行
constructor(){
makeAutoObservable(this)
}
@action
increment=()=>{
this.count++
}
}
const store=new Store()
autorun(()=>{
console.log(store.foo,store.count,'fffrrrf');
})
//修改数据成员,适用于不定义action
runInAction(()=>{
store.count=10
store.foo='hello'
})
@observer
class App extends React.Component{
render(){
const {store}=this.props
return(
<div>
<h1>我是一个仓库</h1>
<h2>{store.count}</h2>
<button onClick={store.increment}>增加</button>
</div>
)
}
}
ReactDOM.render(
<App store={new Store()}/>,
document.getElementById('root')
);
3.4 监听数据
3.4.1 autorun
auto可以从外部检测被observable修饰后数据的改变,如果你想创建一个响应式函数,而该函数本身永远不会有观察者时,可以使用 mobx.autorun。 这通常是当你需要从反应式代码桥接到命令式代码的情况,例如打印日志、持久化或者更新UI的代码。
import {observable,action,makeAutoObservable, autorun} from 'mobx'
autorun(()=>{
//当store.count发生改变。他会自动触发,需要给数据成员加上@observable
console.log(store.count,'gggg');
console.log(store.foo,'gggg');
}
3.4.2 when
when 观察并运行给定的 predicate,直到返回true。 一旦返回 true,给定的 effect 就会被执行,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序
//当count大于等于100的时候,只执行一次自定义逻辑
//如果条件满足一上来就会执行,只会执行一次
when(
()=>{
return store.count>=100
},
()=>{
console.log('when=>',store.count);
}
)
3.reac
3.4.3 reaction
我们可以使用该reaction函数来观察可观察变量的变化。它不会在最初创建时监视该值。reaction接受一个回调来观察值并返回我们想要的东西。第二个参数采用一个值,该值采用第一个函数的返回值,然后我们可以对其执行一些副作用。第三个参数采用一个具有多种选项的对象。
import { reaction, observable } from "mobx";
const todos = observable([
{
title: "eat"
},
{
title: "drink"
}
]);
reaction(
() => todos.map(todo => todo.title),
titles => console.log(titles.join(", "))
);
todos.push({ title: "play" });
3.5 Mobx与React Hooks结合使用
对于 React,我们通过
mobx-react
包获得官方绑定。但是对于hooks,我们需要使用另一个库mobx-react-lite。它为我们提供了自定义hooks,我们可以直接使用它在我们的组件中创建可观察对象。
import React from 'react'
import ReactDOM from 'react-dom'
import { observer, useObservable } from 'mobx-react-lite'
import TodoList from './components/TodoList'
import Footer from './components/Footer'
export const App = observer(() => {
//useObservable为我们提供了一种在一个对象中创建可观察对象、动作和计算属性的新方法。
//可以在此对象上访问所需的值,并且组件通过observer装饰器对数据变化做出反应。
const store = useObservable({
todos: [
{ id: 1, text: 'i am ws', completed: true },
{ id: 2, text: 'i love qinying', completed: false }
],
toggleTodo(index) {
store.todos[index].completed = !store.todos[index]
.completed
},
get remainingTodos() {
return store.todos.filter(t => !t.completed).length
}
})
return (
<div className="App">
<h2>这是一个todolist</h2>
<TodoList
todos={store.todos}
toggleTodo={store.toggleTodo}
/>
<Footer
remaining={store.remainingTodos}
total={store.todos.length}
/>
</div>
)
})
ReactDOM.render(<App />,document.getElementById('root'))
四.记录
本人是一个菜鸟,保持着菜鸟的心态写下此文,这篇文档为参考官网以及学习过程中的一些探索,写这篇文档目的是趁着工作项目闲暇期目对mobx基础知识进行一个回顾与学习记录,不复杂,适合新手教学。