我正在努力使我的反应应用程序尽可能干燥,对于常见的事情,例如使用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”:
答案 0 :(得分:2)
没有阅读整个要点,但是如果你想在现有对象上声明一个新的可观察属性,使用extendObservable
,observable
只创建一个盒装的可观察对象,所以你有一个可观察的< 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添加新的属性。