1

我正在尝试使我的 react 应用程序尽可能干燥,对于诸如使用 rest api 之类的常见事情,我创建了充当具有预定义操作的商店的类,以便于修改它。

看,大代码:

import {autorun, action, observable} from 'mobx'

export function getResourceMethods(name) {
  let lname = name.toLowerCase()
  let obj = {
    methods: {
      plural: (lname + 's'),
      add: ('add' + name),
      addPlural: ('add' + name + 's'),
      rename: ('rename' + name),
    },
    refMethods: {
      add: ('add' + name + 'ByRef'),
      addPlural: ('add' + name + 'sByRef'),
      rename: ('rename' + name + 'ByRef'),
      setRef: ('set' + name + 'Ref'),
    },
    fetchMethods: {
      pending: (lname + 'Pending'),
      fulfilled: (lname + 'Fulfilled'),
      rejected: (lname + 'Rejected'),
    }
  }
  return obj
}

class ResourceItem {
  @observable data;
  @observable fetched = false;
  @observable stats = 'pending';
  @observable error = null;

  constructor(data) {
    this.data = data;
  }
}

class ResourceList {
  @observable items = [];
  @observable fetched = false;
  @observable status = 'pending';

  constructor(name) {
    this['add' + name + 's'] = action((items) => {
      items.forEach((item, iterator) => {
        this.items.push(item.id)
      })
    })
  }
}

class ResourceStore {
  constructor(name, resourceItem, middleware) {
    let {methods} = getResourceMethods(name)

    this.middleware = middleware || []

    let items = methods.plural.toLowerCase()
    this[items] = observable({}) // <--------------- THIS DOES NOT WORK!

    // Add resource item
    this[methods.add] = action((id, resource) => {
      let item = this[items][id], data;

      if (item && item.fetched) {
        data = item.data
      } else {
        data = resource || {}
      }

      this[items][id] = new resourceItem(data)
      this.runMiddleware(this[items][id])
    })

    // Add several resource items
    this[methods.addPlural] = action((resources) => {
      resources.forEach((resource, iterator) => {
        this[methods.add](resource.id, resource)
      })
    })

    // Rename resource item
    this[methods.rename] = action((oldId, newId) => {
      let item = this[items][oldId]
      this[items][newId] = item

      if (oldId !== newId) {
        delete this[items][oldId]
      }
    })
    // Constructor ends here
  }

  runMiddleware(item) {
    let result = item;
    this.middleware.map(fn => {
      result = fn(item)
    })

    return result
  }
}

class ReferencedResourceStore extends ResourceStore {
  @observable references = {}
  constructor(name, resource, middleware) {
    super(name, resource, middleware)

    let {methods, refMethods, fetchMethods} = getResourceMethods(name)

    let getReference = (reference) => {
      return this.references[reference] || reference
    }

    this[refMethods.setRef] = action((ref, id) => {
      this.references[ref] = id
    })

    this[refMethods.add] = action((ref, data) => {
      this[methods.add](getReference(ref), data)
      this[refMethods.setRef](ref, getReference(ref))
    })

    this[refMethods.rename] = action((ref, id) => {
      this[methods.rename](getReference(ref), id)
      this[refMethods.setRef](ref, id)
    })

    // *** Fetch *** //

    // Resource pending

    this[fetchMethods.pending] = action((ref) => {
      this[refMethods.add](ref)
    })

    // Resource fulfilled

    this[fetchMethods.fulfilled] = action((ref, data) => {
      this[refMethods.add](ref, data)
      this[refMethods.rename](ref, data.id)

      let item = this[methods.plural][data.id];
      item.fetched = true
      item.status = 'fulfilled'
    })

  }
}

export {ResourceItem, ResourceList, ResourceStore, ReferencedResourceStore}

现在我只是创建一个简单的用户商店:

class UserResource extends ResourceItem {
  constructor(data) {
    super(data)
  }

  @observable posts = new ResourceList('Posts')
  @observable comments = new ResourceList('Comment')
}

// Create store
class UserStore extends ReferencedResourceStore {}
let store = new UserStore('User', UserResource)

并且mobx-react可以很好地连接到商店,也可以阅读它。但是,每当我对项目(users在这种情况下,属性的名称是动态的)属性进行任何更改时,都没有反应。我还注意到,在 chrome 中,对象属性在树视图中没有“调用属性获取器”:

在此处输入图像描述

4

3 回答 3

2

没有阅读完整的要点,但是如果你想在现有对象上声明一个新的可观察属性, useextendObservable只会observable创建一个装箱的可观察对象,所以你现在有一个可观察的,但还没有一个可观察的属性。换句话说:

this[items] = observable({}) // <--------------- THIS DOES NOT WORK!

应该:

extendObservable(this, {
  [items] : {}
})

注意,如果你不能使用上面的 ES6 语法,它会取消:

const newProps = {}
newProps[items] = {}
extendObservable(this, newProps)

了解这个:https ://mobxjs.github.io/mobx/best/react.html

编辑:哎呀误读了,你已经这样做了,这不是hacky,而是正确的解决方案,只需确保在使用属性之前完成扩展:)

于 2016-10-07T08:09:21.320 回答
1

我找到了一个 hacky 解决方案:

首先,extendObservable改为使用(这是正确的解决方案),然后使用新版本的对象并将其设置为属性。

let items = methods.plural.toLowerCase()
    extendObservable(this, {
      [items]: {}
    })

    // Add resource item
    this[methods.add] = action((id, resource) => {
      let item = this[items][id], data;

      if (item && item.fetched) {
        data = item.data
      } else {
        data = resource || {}
      }

      this[items][id] = new resourceItem(data)
      this.runMiddleware(this[items][id])

      this[items] = {...this[items]}
    })

这行得通,不确定是否有更好的解决方案。

于 2016-10-06T22:05:49.260 回答
1

您的选择是使用extendObservable或使用observable map。有关参考,请参阅observable的文档,特别是:

要创建动态键控对象,请使用 asMap 修饰符!只有对象上最初存在的属性才会成为可观察的,尽管可以使用 extendObservable 添加新属性。

于 2016-10-07T07:00:48.093 回答