思路

umi 3.0+ 之后,有了一个@umijs/plugin-model 插件,可以替代繁琐的 dva 调用方式,利用hook的形式导出一个全局的状态组件.

官方给出的例子是这样的:

//src/models/useAuthModel.js
import { useState, useCallback } from 'react'

export default function useAuthModel() {
  const [user, setUser] = useState(null)

  const signin = useCallback((account, password) => {
    // 可以做一些请求相关的事情
  }, [])

  return {
    user,
    signin,
    signout
  }
}

//login.js
import { useModel } from 'umi';

export default () => {
  const { user, fetchUser } = useModel('user', model => ({ user: model.user, fetchUser: model.fetchUser }));
  return <>hello</>
};

可以看出来,无论在任何一个组件里,只要使用 useModel 引用一下,就可以实现对定义的hook内状态进行调用.

利用这个原理,可以在 src/models 目录下定义一个提供导入的 mobx 状态组件,然后无需引用 Provider 即可实现共享内部的状态.

实现

首先在src/models 目录下定义一个 store

// src/models/useLogin.js
import { flow } from 'mobx'
import { useLocalObservable } from 'mobx-react'

export default function useLogin() {
  const store = useLocalObservable(() => ({
    loading: false,
    setLoading: params => {
      store.loading = params
    },
    verifyLogin: flow(function* (params, callback) {
      store.setLoading(true)
      try {
        const res = yield userAPI.logIn(params)
        if (res) {
          showSuccess('登录成功')
          callback
        }
      } catch (e) {
        showError('账号密码匹配失败')
        store.setLoading(false)
      }
    }),
  }))

  return {
    store
  }
}

在这个组件里,利用 mobx-reactuseLocalObservable 创建一个 observable 对象,并导出供其他组件引用.

里面可以定义所有class类型写法的API,比如 computed 或者 Action .如果是异步调用的函数,可以用 flow 包裹起来,写法上就像 dva 里的 effects .

然后就是像正常的使用一个hook函数一样调用就好了.比起class形式的装饰器调用方式,这种写法更加优雅,逻辑上也更清晰一些:

import React from 'react'
import { observer } from 'mobx-react'

const Login = props => {

  const {store: { loading }} = useModel('useLogin', model => ({ store: model.store }))

  return (
    <div className="login">...</div>
  )
}

export default observer(Login)

后记

不知道这种写法有什么问题,我用起来反正比写 useState 好用多了.

继续观察吧.