MobX没有在我的班级中正确设置可观察性

时间:2016-10-06 21:05:00

标签: javascript reactjs mobx

我正在努力使我的反应应用程序尽可能干燥,对于常见的事情,例如使用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中,object属性在树视图中没有“invoke property getter”:

enter image description here

3 个答案:

答案 0 :(得分:2)

没有阅读整个要点,但是如果你想在现有对象上声明一个新的可观察属性,使用extendObservableobservable只创建一个盒装的可观察对象,所以你有一个可观察的< em> value 现在,但还不是一个可观察的属性。换句话说:

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

应该是:

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

N.b。如果你不能使用上面的ES6语法,那就去了:

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

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

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

答案 1 :(得分: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]}
    })

这很有效,不确定是否有更好的解决方案。

答案 2 :(得分:1)

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

  

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