Twlig / issuesBlog

MIT License
3 stars 0 forks source link

mobx #104

Open Twlig opened 2 years ago

Twlig commented 2 years ago

mobx

mobx是一个集中式状态管理库,用于解决组件间状态(State)共享问题。

mobx中有三个重要的概念:State,Actions,Derivations。

  1. State是状态信息。使用 Proxy 包装。

  2. Actions是动作,其实就是一个函数,调用Action更新State

  3. Derivations是派生信息,是由State状态衍生出来的,比如在State中有用户的fristName和lastName,fullName属性应该是通过state中的fristName和lastName派生出来。

    Derivations有两种:

    • Computed values,总是可以通过纯函数从当前的可观测 State 中派生。
    • Reactions, 当 State 改变时需要自动运行的副作用 (命令式编程和响应式编程之间的桥梁)

注意:如果在state中直接存储fullName,会导致变更firstName的同时,去触发fullName的变更,才能保证数据的统一性。因此,直接派生fullName是更合理的做法。

基础用法

  1. makeObservable

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

class Doubler {
    value
    constructor(value) {
        //参数1: target 把谁变成响应式(可观察)
        //参数2: 指定哪些属性或者方法变成可观察
        makeObservable(this, {
            value: observable,
            double: computed,
            increment: action.bound,//绑定this,使用的时候可以不用处理this指向问题
            fetch: flow  //创建一个 flow 管理异步进程
        })
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
    }
    *fetch() {
        const response = yield fetch("/api/value")
        this.value = response.json()
    }
}
  1. makeAutoObservable

import { makeAutoObservable } from "mobx"

class Doubler {
    value
    constructor(value) {
        //参数1: target 把谁变成响应式(可观察)
        //参数2: 排除不需要被观察的属性或者方法
        //参数3: 选项,如是否自动绑定this
        makeAutoObservable(
            this, 
            {double: false,}, //double排除,不观察
            {autoBind: true}
        )
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
    }
    *fetch() {
        const response = yield fetch("/api/value")
        this.value = response.json()
    }
}

function createDoubler(value) {
    return makeAutoObservable({
        value,
        get double() {
            return this.value * 2
        },
        increment() {
            this.value++
        }
    })
}
  1. observable

与第一个例子中的 makeObservable 不同,observable 支持为对象添加(和删除)字段。 这使得 observable 非常适合用于像动态键控的对象、数组、Maps 和 Sets 之类的集合。

import { observable } from "mobx"

const todosById = observable({
    "TODO-123": {
        title: "find a decent task management system",
        done: false
    }
})

todosById["TODO-456"] = {
    title: "close all tickets older than two weeks",
    done: true
}

const tags = observable(["high prio", "medium prio", "low prio"])
tags.push("prio: for fun")
  1. action

使用这个工具函数来创建一个会被立即调用的临时 action。一次

import { observable, runInAction } from "mobx"

const state = observable({ value: 0 })

runInAction(() => {
    state.value++
    state.value++
})

在处理 Promise 时,更新 state 的处理程序应该被 action 包装起来,或者被标记为 actions。

import { action, makeAutoObservable } from "mobx"

class Store {
    githubProjects = []
    state = "pending" // "pending", "done" or "error"

    constructor() {
        makeAutoObservable(this)
    }

    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(
            action("fetchSuccess", projects => {
                const filteredProjects = somePreprocessing(projects)
                this.githubProjects = filteredProjects
                this.state = "done"
            }),
            action("fetchError", error => {
                this.state = "error"
            })
        )
    }
}
  1. reactions

reactions 是需要理解的重要概念,因为他可以将 MobX 中所有的特性有机地融合在一起。 reactions 的目的是对自动发生的副作用进行建模。 它们的意义在于为你的可观察状态创建消费者,以及每当关联的值发生变化时,自动运行副作用。

reactions使用原则:

observer

mobx负责做状态管理,那么状态变更如何触发视图的更新呢? mobx-react或者mobx-react-lite库的observer HOC 提供了状态更新触发视图更新的功能。

observer 将自动订阅 React components 中任何 在渲染期间 被使用的 可被观察的对象 。 因此, 当任何可被观察的对象 变化 发生时候 组件会自动进行重新渲染(re-render)。 它还会确保组件在 没有变化 发生的时候不会进行重新渲染(re-render)。 但是, 更改组件的可观察对象的不可读属性, 也不会触发重新渲染(re-render)。

import {observer} from 'mobx-react-lite'
import {createContext, useContext} from "react"

const TimerContext = createContext<Timer>()

const TimerView = observer(() => {
    // 从context中获取timer.
    const timer = useContext(TimerContext) // 可以在上面查看 Timer的定义。
    return (
        <span>Seconds passed: {timer.secondsPassed}</span>
    )
})

ReactDOM.render(
    <TimerContext.Provider value={new Timer()}>
        <TimerView />
    </TimerContext.Provider>,
    document.body
)