编写异步 Actions (动作)

    上面的示例会抛出异常,因为传给 fetchGithubProjectsSomehow promise 的回调函数不是 fetchProjects 动作的一部分,因为动作只会应用于当前栈。

    首选的简单修复是将回调函数变成动作。(注意使用 action.bound 绑定在这很重要,以获取正确的 this!):

    1. mobx.configure({ enforceActions: true })
    2. class Store {
    3. @observable githubProjects = []
    4. @observable state = "pending" // "pending" / "done" / "error"
    5. @action
    6. fetchProjects() {
    7. this.githubProjects = []
    8. this.state = "pending"
    9. fetchGithubProjectsSomehow().then(this.fetchProjectsSuccess, this.fetchProjectsError)
    10. }
    11. @action.bound
    12. fetchProjectsSuccess(projects) {
    13. const filteredProjects = somePreprocessing(projects)
    14. this.githubProjects = filteredProjects
    15. this.state = "done"
    16. }
    17. @action.bound
    18. this.state = "error"
    19. }
    20. }

    内联动作的缺点是 TypeScript 无法对其进行类型推导,所以你应该为所有的回调函数定义类型。你还可以只在动作中运行回调函数中状态修改的部分,而不是为整个回调创建一个动作。这种模式的优势是它鼓励你不要到处写 action,而是在整个过程结束时尽可能多地对所有状态进行修改:

    1. mobx.configure({ enforceActions: true })
    2. class Store {
    3. @observable githubProjects = []
    4. @observable state = "pending" // "pending" / "done" / "error"
    5. @action
    6. fetchProjects() {
    7. this.githubProjects = []
    8. this.state = "pending"
    9. fetchGithubProjectsSomehow().then(
    10. projects => {
    11. const filteredProjects = somePreprocessing(projects)
    12. // 将‘“最终的”修改放入一个异步动作中
    13. runInAction(() => {
    14. this.githubProjects = filteredProjects
    15. this.state = "done"
    16. })
    17. },
    18. error => {
    19. // 过程的另一个结局:...
    20. runInAction(() => {
    21. })
    22. }
    23. )
    24. }
    25. }

    注意,runInAction 还可以给定第一个参数作为名称。runInAction(f) 实际上是 action(f)() 的语法糖。

    然而,更好的方式是使用 flow 的内置概念。它们使用生成器。一开始可能看起来很不适应,但它的工作原理与 async / await 是一样的。只是使用 function * 来代替 async,使用 yield 代替 await 。使用 flow 的优点是它在语法上基本与 async / await 是相同的 (只是关键字不同),并且不需要手动用 @action 来包装异步代码,这样代码更简洁。

    flow 只能作为函数使用,不能作为装饰器使用。flow 可以很好的与 MobX 开发者工具集成,所以很容易追踪 async 函数的过程。

    1. mobx.configure({ enforceActions: true })
    2. class Store {
    3. @observable githubProjects = []
    4. @observable state = "pending"
    5. fetchProjects = flow(function * () { // <- 注意*号,这是生成器函数!
    6. this.githubProjects = []
    7. this.state = "pending"
    8. try {
    9. const projects = yield fetchGithubProjectsSomehow() // 用 yield 代替 await
    10. const filteredProjects = somePreprocessing(projects)
    11. // 异步代码块会被自动包装成动作并修改状态
    12. this.state = "done"
    13. this.githubProjects = filteredProjects
    14. } catch (error) {
    15. this.state = "error"
    16. }
    17. })