三小时带你玩转mobx----笔记

mobx

1. MobX基本概念

mobx是一个简单、可扩展的状态管理工具,通过运用透明的函数式响应编程使状态管理变得简单。
特点:

  • 无模版代码,非常简洁;
  • 数据是响应式的,可以直接修改数据(Proxy);
  • 可以直接处理异步;
  • 适合简单、规模不大的应用。(redux约束强,更适合大型多人协作开发)

版本说明

  • Mobx4可以运行在任何支持ES5语法的浏览器;
  • Mobx5版本运行在任何支持ES6语法的浏览器;
  • Mobx4和Mobx5具有相同的api,都需要使用装饰器语法;
  • Mobx6是目前最新的版本,为了兼顾与标准JavaScript的最大兼容性,默认情况下放弃了装饰器语法。

2. MobX基本使用

2.1 安装环境

初始化项目:
npx create-react-app mobx-demo --template typescript
安装库 mobx、mobx-react或者mobx-react-lite(只支持函数组件)
yarn add mobx mobx-react

2.2 mobx基本使用

**observable**定义一个存储state的可追踪字段(Proxy)。
**action**将一个方法标记为可修改state的action。
**computed**标记一个可以由state派生出新值并且缓存其输出的计算属性。
工作流程:
在这里插入图片描述

2.3 创建store

  • 新建文件store/Counter.ts,通过class创建一个Counter类;
  • 使用makeObservable将类的属性和方法变成响应式的;
// 参数1:target把谁变成响应式(可观察)
    // 参数2: 指定哪些属性或方法变成可观察
    makeObservable(this, {
      count: observable,  // 属性是observable的
      increment: action.bound,
      decrement: action.bound,
      reset: action,    // 如果要改数据,那么这个方法一定要标为action
      double: computed
    });
  • 导出counter实例;
  • 注意:mobx中每一个store都只应该初始化一次。

2.4 在组件中使用

  • 从mobx-react-lite或mobx-react库中引入observer高阶组件函数;
  • 使用observer高阶组件函数包裹需要使用store的组件;
  • 引入store对象;
  • 使用store对象的属性和方法即可。

2.5 处理this指向

默认class中的方法不会绑定this,this指向取决于如何调用

makeObservable(this, {
      count: observable,  // 属性是observable的
      increment: action,
      decrement: action,
      reset: action    // 如果要改数据,那么这个方法一定要标为action
    });
<button onClick={() => counter.increment()}>1</button> // 正确
<button onClick={counter.increment>1</button> // 错误

在使用makeObservable时可以通过action.bound绑定this指向。

makeObservable(this, {
      count: observable,  // 属性是observable的
      increment: action.bound,
      decrement: action.bound,
      reset: action    // 如果要改数据,那么这个方法一定要标为action
    });
<button onClick={counter.increment}>1</button>

2.6 计算属性的使用

computed可以用来从其他可观察对象中派生信息。

  • 计算值采用惰性求值,会缓存其输出,并且只有当其依赖的可观察对象被改变时才会重新计算。
  • 计算属性是一个方法,且方法前面必须使用get进行修饰
  • 计算属性还需要通过makeObservable方法指定。
  get double() {
    return this.count * 2;
  }

2.7 makeAutoObservable的使用

makeAutoObservable就像是加强版的makeObservable。在默认情况下它将推断所有的属性。

 // 参数1:target 让哪个对象变成可观察
 // 参数2: 排除属性和方法,不让其成为可观察的
 // 参数3: 指定自动绑定this
 makeAutoObservable(this, {}, { autoBind: true })

推断规则如下:

  • 所有的属性都成为observable;
  • 所有的方法都成为action;
  • 所有的get都成为computed。

通过overrides排除不需要被观察的属性和方法:

constructor() {
    makeAutoObservable(this, { decrement: false })
}

通过autoBind可以绑定this指向:

constructor() {
    makeAutoObservable(this, { decrement: false }, { autoBind: true })
}
<button onClick={counter.increment}>1</button>

不写{ autoBind: true }时,button中的点击回调函数应该这样写

constructor() {
    makeAutoObservable(this, { decrement: false })
}
 <button onClick={() => counter.increment()}>1</button>

3. MobX监听属性

3.1 autoRun的使用

autoRun函数接受一个函数作为参数,每当该函数所观察的值发生变化时,它都应该运行。
当你自己创建autoRun时,它也会运行一次。
MobX会自动收集并订阅所有的可观察属性,一旦有改变发生,autoRun将会再次触发。

import { makeAutoObservable, autorun } from 'mobx'

class Counter {
  constructor() {
    // 参数1:target 让哪个对象变成可观察
    // 参数2: 排除属性和方法,不让其成为可观察的
    // 参数3: 指定自动绑定this
    makeAutoObservable(this, {}, { autoBind: true })
  }
  count = 0;
  increment() {
    this.count++;
  }
  decrement() {
    this.count--;
  }
  reset() {
    this.count = 0;
  }
  get double() {
    return this.count * 2;
  }
}

const counter = new Counter();
// 只要count发生变化,autorun就会自动执行
// 如果里面有很多个属性,只要其中一个发生变化,autorun里的内容就会全部执行一次
autorun(() => {
  console.log('counter.count', counter.count);
})

export default counter;  // 导出了一个对象

3.2 reaction的使用

reaction类似于autorun,但可以让你更加精细地控制要跟踪的可观察对象。
它接受两个函数作为参数,

  • 参数1:data函数,其返回值将会作为第二个函数输入
  • 参数2:回调函数

与autoRun不同,reaction在初始化时不会自动运行

import { reaction } from 'mobx'

reaction(
  () => counter.count,
  (value, oldValue) => {
    console.log('counter.count发生了变化', value, oldValue)
  }
)

4. MobX处理异步

4.1 MobX如何处理异步

异步进程在mobx中不需要任何特殊处理,因为不论是何时引发的,所有reactions都将会自动更新。
因为可观察对象是可变的,因此在action执行过程中保持对它们的引用一般是安全的。
如果可观察对象的修改不是在action函数中,控制台会报警告(可以关闭,但是不推荐)。

示例:
想让点击+1的时候,过1秒count再+1

constructor() {
    makeAutoObservable(this, { decrement: false })
}

increment() {
    setTimeout(() => {
      this.count++;
    }, 1000)
 }

上面代码功能是正常的,但是会报警告:
[MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: Counter@1.count

原因是可观察的值的修改,应该在action函数中(如increment()函数)进行,但是实际上却是在延时函数setTimeout()中进行的。

想要不看到这个警告,有两种方法:

  • 关掉警告(不推荐!!)
import { configure } from 'mobx'

configure({
  enforceActions: 'never'
})
  • 换一种写法,将同步和异步拆开
increment() {
    this.count++;
  }
incrementAsync() {
  setTimeout(this.increment, 1000)
}
<button onClick={() => counter.incrementAsync()}>1</button>

注意: 修改状态一定要在action函数中进行

4.2 runInAction

通过runInAction可以保证所有异步更新可观察对象的步骤都应该标识为action。

import { runInAction } from 'mobx'


 increment() {
    setTimeout(() => {
      runInAction(() => {
        this.count++;
      })

    }, 1000)
  }

5. MobX模块化

5.1 多个store的场景

项目规模变大后,不能将所有的状态和方法都放到一个store中。
我们可以根据业务模块定义多个sore。

import counter from "./store/Counter";
import { observer } from 'mobx-react';
import cart from './store/Cart';
// observer是一个高阶组件函数,需要包裹一个组件,这样这个组件才会更新

function App() {
  return (
    <div className="App">
      <h3>计数器案例</h3>
      <div>点击次数:{counter.count}</div>
      <div >double: {counter.double}</div>
      <button onClick={() => counter.increment()}>1</button>
      <button onClick={() => counter.decrement()}>1</button>
      <button onClick={() => counter.reset()}>重置</button>
      <div>{cart.list}</div>
    </div>
  );
}

export default observer(App);

通过一个根store统一管理所有的store。

5.2 实现步骤

  • 拆分Counter和Cart两个Store,每个Store都可以有自己的state、action、computed;
  • 在store/index.ts中导入所有的Store,组合成一个Store;
  • 使用useContext机制,自定义useStore hook,统一导出Store。
  • createContext作用是创建一个上下文对象,用于跨组件通讯
import { createContext, useContext } from 'react'
import cart from './Cart'
import counter from './Counter'

class RootStore {
  cart = cart;
  counter = counter;
}

const store = new RootStore()

// 创建一个上下文对象,用于跨组件通讯
const Context = createContext(store)

// 自定义hooks
export default function useStore() {
  return useContext(Context)
}

调用store

// import counter from "./store/Counter";
// import cart from './store/Cart';
import { observer } from 'mobx-react';
// observer是一个高阶组件函数,需要包裹一个组件,这样这个组件才会更新
import useStore from './store'

function App() {
  const { counter, cart } = useStore()  // 解构
  return (
    <div className="App">
      <h3>计数器案例</h3>
      <div>点击次数:{counter.count}</div>
      <div >double: {counter.double}</div>
      <button onClick={() => counter.increment()}>1</button>
      <button onClick={() => counter.decrement()}>1</button>
      <button onClick={() => counter.reset()}>重置</button>
      <div>{cart.list}</div>
    </div>
  );
}

export default observer(App);

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值