我无法在react / mobx应用程序中找到解决特定问题的好方法。假设我有一些盒子,我想在屏幕上以某种布局定位,其中每个盒子的位置取决于所有其他盒子的位置(想想力导向布局或类似)。还会根据其他@observable
变量进行重新布局,例如sorting
按框大小或filtering
按类别划分。此外,这些框是交互式的,如果用鼠标悬停,则获得highlighted
。我也希望这可以在框数据中反映为框上设置的highlighted
属性。
来自redux,我最初的方法是拥有一些可观察的ui状态变量,例如sortBy
和filterBy
,然后有一个@compute
getter返回每当其中一个变量发生变化时,设置了新鲜框对象及其positions
和highlighted
属性的新鲜。
const boxes = [{ id: '1', category: 'bla', size: 5 }, ...];
class UiStore {
@observable sortBy = 'size';
@observable filterBy = undefined;
@observable hoveredBox = undefined;
/* Some @action(s) to manipulate the properties */
}
const uiStore = new UiStore();
class BoxesStore {
@computed
get positionedBoxes() {
const filteredBoxes = boxes.filter(box =>
box.category === uiStore.filterBy
);
// An array of positions based on the filtered boxes etc.
const positions = this.calculateLayout(filteredBoxes);
return boxes.map((box, i) => {
const { x, y } = positions[i];
return {
...box,
highlighted: uiStore.hoveredBox === box.id,
x,
y,
};
});
}
}
理论上这很好用。不幸的是,我不认为这是性能方面最有效的解决方案。有很多方框,在每次更改和每次悬停时创建新对象都不是最佳解决方案。同样根据mobx best practices,建议对整个主题略有不同的思考。我理解它的方式,在mobx中你应该创建每个都有@observable
个属性的盒子的实际实例。另外一个应该只在盒子中保存每个盒子的一个实例。例如,如果我要添加一个额外的@computed
getter来计算不同类型的布局,我现在在技术上持有我商店中同一个盒子的多个版本,对吧?
因此,我尝试找到一个解决方案,为每个包含Box
属性的@computed
getter的框项目创建highlighted
类实例。这是有效的,但我不知道如何以及何时设置盒子的位置。一种方法是采用与上述方法类似的方法。计算@computed
吸气剂中的位置,然后在同一函数中设置每个盒子的x,y位置。这可能有效,但在@compute中设置位置肯定是错误的。
const boxes = [{ id: '1', category: 'bla', size: 5 }, ...];
/* uiStore... */
class Box {
constructor({id, category, size}) {
this.id = id;
this.category = category;
this.size = size;
}
@computed
get highlighted() {
return this.id === uiStore.highlighted
}
}
class BoxesStore {
@computed
get boxes() {
return boxes.map(box => new Box(box));
}
@computed
get positionedBoxes() {
const filteredBoxes = this.boxes.filter(box =>
box.category === uiStore.filterBy
);
// An array of positions based on the filtered boxes etc.
const positions = this.calculateLayout(filteredBoxes);
const positionedBoxes = filteredBoxes.map(box => {
const { x, y } = positions[i];
box.x = x;
box.y = y;
})
return positionedBoxes;
}
}
另一种方法是计算boxStore上@compute中的所有位置,然后在框内访问这些位置。
const boxes = [{ id: '1', category: 'bla', size: 5 }, ...];
/* uiStore... */
class Box {
constructor({id, category, size}, parent) {
this.id = id;
this.category = category;
this.size = size;
this.parent = parent;
}
@computed
get position() {
return _.find(this.parent.positionedBoxes, {id: this.id})
}
}
class BoxesStore {
@computed
get boxes() {
return boxes.map(box => new Box(box));
}
@computed
get positionedBoxes() {
const filteredBoxes = this.boxes.filter(box =>
box.category === uiStore.filterBy
);
// An array of positions based on the filtered boxes etc.
return this.calculateLayout(filteredBoxes);
}
}
同样,这可能有效,但感觉非常复杂。作为最后一种方法,我正在考虑使用mobx反应或自动运行来监听uiStore中的更改,然后通过类似的操作设置框的位置:
const boxes = [{ id: '1', category: 'bla', size: 5 }, ...];
/* uiStore... */
class Box {
@observable position = {x: 0, y: 0};
@action
setPosition(position) {
this.position = position;
}
}
class BoxesStore {
constructor() {
autorun(() => {
const filteredBoxes = this.boxes.filter(box =>
box.category === uiStore.filterBy
);
this.calculateLayout(filteredBoxes).forEach((position, i) =>
filteredBoxes[i].setPosition(position);
)
})
}
@computed
get boxes() {
return boxes.map(box => new Box(box));
}
}
到目前为止,我最喜欢这种方法,但我觉得这些解决方案中的任何一种都不是理想的,也不是优雅的。我是否错过了一些明显的解决此类问题的方法?是否有更简单,更实用的方法来解决这个问题?
感谢您阅读以下所有内容:)
答案 0 :(得分:0)
根据calculateLayout
的实施情况,我建议使用computed
或派生&观察位置。
首先,我将保留您观察到的数据结构,而不是计算框,因此不需要在更改时重新创建类:
class BoxStore {
@observable
boxes;
}
如果calculateLayout()只是根据前一个Box设置x和y的坐标,我建议只使用链表结构:
class Box {
@observable prevBox;
@computed
get position()
if(this.prevBox)
return this.prevBox.clone().add(10,20);
return {x:0,y:0};
}
}
如果计算很复杂并且只需要在添加或删除框时部分重新计算,我建议使用属性观察者:https://mobx.js.org/refguide/observe.html#observe
class BoxStore {
@observable
boxes = [];
constructor(){
mobx.observe(this.boxes, (change) => {
// efficiently compute new locations based on changes here.
})
}
}
通过这种方式,您可以获得每次都不会完全重新计算的优势。但有一个优势,即该位置仍然是“派生”的。 (你不必手动设置它)。一旦你得到了正确的公式,你就再也不用考虑它了。