Mobx6使用与老版本的区别

820 阅读4分钟

mobx是和react搭配使用的状态管理库,没有redux那么多的概念和编码规范。

随着官方的升级,mobx6的用法和mobx5有了稍微不同的区别。

核心API的用法变动

observable state

把对象,数组,Maps 和 Sets 都可以被转化为可观察对象。

在mobx5中的用法

import { observable, computed } from "mobx";

class OrderLine {
    @observable price = 0;
    @observable amount = 1;

    @computed get total() {
        return this.price * this.amount;
    }
}

在mobx6中, 不再需要装饰器这种写法了,搭配react17会报错。
mobx6,可以使用makeObservable在类的构造函数中使用。

import { makeObservable, observable, computed, action, flow } from "mobx"

class Doubler {
    value

    constructor(value) {
        makeObservable(this, {
            value: observable,
            double: computed,
            increment: action,
            fetch: flow
        })
        this.value = value
    }

    get double() {
        return this.value * 2
    }

    increment() {
        this.value++
    }

    *fetch() {
        const response = yield fetch("/api/value")
        this.value = response.json()
    }
}

action

在mobx5中 action的写法

class Ticker {
    @observable tick = 0

    @action.bound
    increment() {
        this.tick++ // 'this' 永远都是正确的
    }
}

const ticker = new Ticker()
setInterval(ticker.increment, 1000)

在mobx6中,action 也可以放入构造函数constructor中

import { makeObservable, observable, action } from "mobx"

class Doubler {
    value = 0

    constructor(value) {
        makeObservable(this, {
            value: observable,
            increment: action
        })
    }

    increment() {
        // 观察者不会看到中间状态.
        this.value++
        this.value++
    }
}

computed计算值

在mobx5中 computed计算值,同样是加装饰器来处理。
在mobx6中,同样是写在构造函数中

import { makeObservable, observable, computed, autorun } from "mobx"

class OrderLine {
    price = 0
    amount = 1

    constructor(price) {
        makeObservable(this, {
            price: observable,
            amount: observable,
            total: computed
        })
        this.price = price
    }

    get total() {
        console.log("Computing...")
        return this.price * this.amount
    }
}

makeAutoObservable

makeAutoObservable(target, overrides?, options?) makeAutoObservable与makeObservable类似。

自定义observable

在某些情况下,您可能希望有更多的数据结构或其他东西(如流),用于响应式计算。 通过使用原子的概念来实现这一点非常简单。 可以使用Atoms向MobX发出信号,表示某些可观察数据源已经被观察到或已经更改,而MobX将在atom被使用或不再使用时,向atom发出信号.

import { createAtom, autorun } from "mobx"

class Clock {
    atom
    intervalHandler = null
    currentDateTime

    constructor() {
        // 创建一个atom来与MobX进行交互
        
        this.atom = createAtom(
            // 第一个参数是:
            // - Atom的名称 用于在程序debug时便于观察.
            "Clock",
            // 第二个参数是(可选):
            // - 在unobserved 变到 observed时的回调函数
            () => this.startTicking(),
            // 第三个参数是(可选):
            // -  在observed 变到 unobserved时的回调函数
            () => this.stopTicking()
            // 同一个atom在多个状态之间来回变化
        )
    }

    getTime() {
        // 通知MobX这个observable 数据源已经被使用。
        //
        // 如果atom当前状态是 正在被观察(observed),reportObserved将返回true
        // 通过 reaction.在需要时,它会被切换成“startTicking”(开始计时)
        // onBecomeObserved处理函数.
        if (this.atom.reportObserved()) {
            return this.currentDateTime
        } else {
            // 调用了getTime函数,但此时没有任何reaction在运行,隐藏没有人依赖此值。所以并且不会触发startTicking和onBecomeObserved处理程序。
            // 在这种情况下,根据atom的性质,其行为可能会有所不同,例如引发错误,返回默认值等。
            return new Date()
        }
    }

    tick() {
        this.currentDateTime = new Date()
        this.atom.reportChanged() // 通知MobX这个数据源已经更改。
    }

    startTicking() {
        this.tick() // 初始化 tick
        this.intervalHandler = setInterval(() => this.tick(), 1000)
    }

    stopTicking() {
        clearInterval(this.intervalHandler)
        this.intervalHandler = null
    }
}

const clock = new Clock()

const disposer = autorun(() => console.log(clock.getTime()))
// 控制台打印输出运行中的“每一秒”

// 如果没有其他人使用同样的“clock”,它也会停止滴答作响。并且停止在控制的输出
disposer()

创建惰性 observables

  • onBecomeObserved(observable, property?, listener: () => void): (() => void)
  • onBecomeUnobserved(observable, property?, listener: () => void): (() => void)

onBecomeObservedonBecomeUnobserved方法可以给现有的可观察对象附加惰性行为或副作用。它们是MobX可观察系统的钩子并且 当可观察对象开始停止被观察时,它们会得到通知。它们都返回一个用来取消listenerdisposer函数。

export class City {
    location
    temperature
    interval

    constructor(location) {
        makeAutoObservable(this, {
            resume: false,
            suspend: false
        })
        this.location = location
        // 只有在实际使用温度时才开始获取数据!
        onBecomeObserved(this, "temperature", this.resume)
        onBecomeUnobserved(this, "temperature", this.suspend)
    }

    resume = () => {
        log(`Resuming ${this.location}`)
        this.interval = setInterval(() => this.fetchTemperature(), 5000)
    }

    suspend = () => {
        log(`Suspending ${this.location}`)
        this.temperature = undefined
        clearInterval(this.interval)
    }

    fetchTemperature = flow(function* () {
        // 获取数据的逻辑...
    })
}

config 配置

mobx6支持你配置你的使用偏好。这包括:

  • useProxies 是否MobX 只能运行在支持 Proxy的环境中

  • enforceActions enforceActions 配置的目的是让你不会忘记使用 action 包裹事件处理函数

  • computedRequiresReaction 禁止在action或者reaction之外,直接获取未被观察的计算属性的值

import { configure } from "mobx" 

configure({ 
    enforceActions: "always", 
    computedRequiresReaction: true, 
    reactionRequiresObservable: true, 
    observableRequiresReaction: true, 
    disableErrorBoundaries: true 
})

装饰器的未来

在版本6之前,Mobx鼓励使用ES.next中的decorators,将某个对象标记为observablecomputed 和 action。然而,装饰器语法尚未定案以及未被纳入ES标准,标准化的过程还需要很长时间,且未来制定的标准可能与当前的装饰器实现方案有所不同。出于兼容性的考虑,我们在MobX 6中放弃了它们,并建议使用makeObservable / makeAutoObservable代替