状态管理之MobX

775 阅读3分钟

前言

  • mobx-react-lite仅支持函数组件,mobx-react还支持类组件
  • 安装: npm i mobx mobx-react mobx-react-lite

基本使用

  • store/index.ts

    import { makeAutoObservable } from 'mobx'
    
    export default class useMobxStore {
      count: number = 0 // 初始化状态数据
    
      constructor() {
        // 对初始化数据进行响应式处理
        makeAutoObservable(this)
      }
    
      // 设置改变初始化数据方法
      addCount = () => { 
        this.count++
      }
    }
    
  • 组件内部使用

    import React from 'react'
    import { observer } from 'mobx-react-lite' // 从 mobx-react-lite 内部引入 observer 让 mobx 与 react 进行关联
    import useMobxStore from '@/store'
    
    const MobxDemo = () => {
      return (
        <div>
          <h2>{useMobxStore.count}</h2>
          <button onClick={useMobxStore.addCount}>+1</button>
        </div>
      )
    }
    
    export default observer(MobxDemo) // 对组件进行mobx与react的关联绑定
    

异步action

方式1: 使用 action 对它们进行包装

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', res => {
        const filteredProjects = somePreprocessing(res)
        this.githubProjects = filteredProjects
        this.state = 'done'
      }),
      action('fetchError', error => {
        this.state = 'error'
      })
    )
  }
}

方式2: 由 makeAutoObservable 自动包装为 action

如果 Promise 的处理函数是类的字段,它们将由 makeAutoObservable 自动包装为 action

import { makeAutoObservable } from 'mobx'

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

  constructor() {
    makeAutoObservable(this)
  }

  fetchProjects() {
    this.githubProjects = []
    this.state = 'pending'
    fetchGithubProjectsSomehow().then(this.projectsFetchSuccess, this.projectsFetchFailure)
  }

  projectsFetchSuccess = res => {
    const filteredProjects = somePreprocessing(res)
    this.githubProjects = filteredProjects
    this.state = 'done'
  }

  projectsFetchFailure = error => {
    this.state = 'error'
  }
}

方式3: await + runInAction

import { runInAction, makeAutoObservable } from 'mobx'

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

  constructor() {
    makeAutoObservable(this)
  }

  async fetchProjects() {
    this.githubProjects = []
    this.state = 'pending'
    try {
      const res = await fetchGithubProjectsSomehow()
      const filteredProjects = somePreprocessing(res)
      // 修改状态的时候外面包裹runInAction
      runInAction(() => {
        this.githubProjects = filteredProjects
        this.state = 'done'
      })
    } catch (e) {
      runInAction(() => {
        this.state = 'error'
      })
    }
  }
}

方式4: flow + generator

import { flow, makeAutoObservable, flowResult } from 'mobx'

class Store {
  githubProjects = []
  state = 'pending'

  constructor() {
    makeAutoObservable(this, {
      fetchProjects: flow
    })
  }

  // 注意星号, 这是一个 generator 函数
  *fetchProjects() {
    this.githubProjects = []
    this.state = 'pending'
    try {
      // Yield 代替 await.
      const res = yield fetchGithubProjectsSomehow()
      const filteredProjects = somePreprocessing(res)
      this.state = 'done'
      this.githubProjects = filteredProjects
    } catch (error) {
      this.state = 'error'
    }
  }
}

const store = new Store()
const res = await flowResult(store.fetchProjects())

计算属性

计算属性需要用 get 开头然后接函数名,并且mobx规定计算属性必须要有返回值

  • store/index.ts

    import { makeAutoObservable } from 'mobx'
    
    export default class useMobxStore {
      list = [1, 2, 3, 4, 5, 6]
    
      constructor() {
        makeAutoObservable(this)
      }
      // 定义一个计算属性
      get filterList() {
        return this.list.filter((item) => item > 4)
      }
    }
    
  • 组件内部使用

    import React from 'react'
    import { observer } from 'mobx-react-lite'
    import store from '@/store'
    const { useMobxStore } = store
    
    const MobxDemo = () => {
      return (
        <div>
          {/* 计算属性 */}
          <div>{useMobxStore.filterList.join('~')}</div>
        </div>
      )
    }
    
    export default observer(MobxDemo)
    

模块化

  • store/index.ts

    import React from 'react'
    import useMobxStore from './modules/mobxDmeo'
    class RootStore {
      useMobxStore: useMobxStore
    
      constructor() {
        // 对引入进行来的子模块进行实例化操作,并挂载到RootStore上
        this.useMobxStore = new useMobxStore()
      }
    }
    
    // 实例化操作
    const rootStore = new RootStore()
    // 这里可以使用React context 完成统一方法的封装需求
    const context = React.createContext(rootStore)
    // 封装useStore方法,业务组件调用useStore方法便就可以直接获取rootStore
    const useStore = () => React.useContext(context)
    
    export default useStore
    
  • 组件内统一使用

    import React from 'react'
    import { observer } from 'mobx-react-lite'
    import useStore from '@/store'
    
    const MobxDemo = () => {
      /* 注意 此处解构写道store一级即可,如果解构到store下面一级的属性级,则会破坏响应式数据 */
      const { useMobxStore } = useStore()
    
      return (
        <div>
          <h2>{useMobxStore.count}</h2>
          <button onClick={useMobxStore.addCount}>+1</button>
          {/* 计算属性 */}
          <div>{useMobxStore.filterList.join('~')}</div>
          <button onClick={useMobxStore.addList}>点我数组添加内容</button>
        </div>
      )
    }
    
    export default observer(MobxDemo)
    

数据持久化

安装: npm i mobx-persist-store

import { makeAutoObservable } from 'mobx'
import { makePersistable } from 'mobx-persist-store' // 引入makePersistable方法进行持久化存储

export default class useMobxStore {
  count: number = 0

  constructor() {
    // 响应式处理
    makeAutoObservable(this)
    // 数据持久化存储
    makePersistable(this, {
      name: 'mobxDemo', // 存储到localStorage当中的key值是什么
      properties: ['count'], // 需要持久化的数据是什么,此数据需要为上面声明了的变量
      storage: window.localStorage, // 用哪种方式存储
    })
  }

  addCount = () => {
    this.count++
    console.log(this.count)
  }
}

Mobx与redux的区别

  • Mobx写法上更偏向于OOP
  • 对一份数据直接进行修改操作,不需要始终返回一个新的数据
  • 并非单一store,可以多store
  • Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象
  • 优点
    • 学习成本小
    • 面向对象编程, 而且对 TS 友好
  • 缺点
    • 过于自由:Mobx提供的约定及模版代码很少,代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一
    • 相关的中间件很少,逻辑层业务整合是问题

参考