Reputation: 193
Adding a node to a list and yet a component is not re-rendering. Mobx Chrome Extension dev tools says it's a dependency but for some reason still no reaction!
A button renders 'Add' or 'Remove' based on whether a node is in a list. It doesn't re-render unless I move to another component and then open this component again.
Buttons:
@inject("appStore") @observer
class EntityTab extends Component {
...
render() {
return (
...
{/* BUTTONS */}
{ this.props.appStore.repo.canvas.graph.structure.dictionary[id] !== undefined ?
<div onClick={() => this.props.appStore.repo.canvas.graph.function(id)}>
<p>Remove</p>
</div>
:
<div onClick={() => this.props.appStore.currRepo.canvas.otherfunction(id)}>
<p>Add</p>
</div>
}
...
)
}
}
The Add button renders, I click on the button which triggers
this.props.appStore.currRepo.canvas.otherfunction(id)
and then we go to this function
@observable graph = new Graph();
...
@action
otherfunction = (idList) => {
// Do nothing if no ids are given
if (idList === null || (Array.isArray(idList) && idList.length === 0)) return;
// If idList is a single value, wrap it with a list
if (!Array.isArray(idList)) { idList = [idList] }
let nodesToAdd = [];
let linksToAdd = [];
// Add all new links/nodes to graph
Promise.all(idList.map((id) => { return this.getNode(id, 1) }))
.then((responses) => {
for (let i = 0; i < responses.length; i++) {
let res = responses[i];
if (res.success) {
nodesToAdd.push(...res.data.nodes);
linksToAdd.push(...res.data.links);
}
}
this.graph.addData(nodesToAdd, linksToAdd, idList, this.sidebarVisible);
});
};
The getNode function creates new Node objects from the data. For reference, those objects are instantiated as such
export default class Node {
id = '';
name = '';
type = '';
constructor(r) {
for (let property in r) {
// Set Attributes
this[property] = r[property];
}
}
}
anyway, the addToGraphFromIds triggers
this.graph.addData(nodesToAdd, linksToAdd);
and then we go to that function
@action
addData = (nodes, links) => {
this.structure.addNodes(nodes);
this.structure.addLinks(links);
};
which triggers
this.structure.addNodes(nodes);
which leads to this function
@observable map = new Map();
@observable map2 = new Map();
@observable dictionary = {};
@observable dictionary2 = {};
@observable allNodes = [];
@observable allLinks = [];
...
@action
addNodes = (nodes=[]) => {
if (!nodes || nodes.length === 0) return;
nodes = utils.toArray(nodes);
// Only consider each new node if it's not in the graph or a duplicate within the input list
nodes = _.uniqBy(nodes, (obj) => { return obj.id; });
const nodesToConsider = _.differenceBy(nodes, this.allNodes, (obj) => { return obj.id; });
// Add nodes to map
let currNode;
for (let i = 0; i < nodesToConsider.length; i++) {
currNode = nodesToConsider[i];
this.map.set(currNode.id, new Map());
this.map2.set(currNode.id, new Map());
this.dictionary[currNode.id] = currNode;
}
// Update internal list of nodes
this.allNodes = this.allNodes.concat(nodesToConsider);
};
As we can see in the first codebox,
this.props.appStore.repo.canvas.graph.structure.dictionary[id] !== undefined
Should cause the button to change values as we have added the current node. The nodes appear in the dictionary when I log or use mobx chrome extension dev tools, but I have to switch tabs and then the button will re-render. I've tried using other lines like
this.props.appStore.repo.canvas.graph.structure.allNodes.includes(node)
but that doesn't work either. Am absolutely stuck and need help. I have a feeling it has to do with nested observables, and maybe tagging @observable isn't good enough, but not quite sure. repo and canvas are marked as observables and instantiate a new Repo() object and new Canvas() object, much like new Node() is created in getNodes.
Upvotes: 6
Views: 2273
Reputation: 343
Mobx (v4) does not track addition or removal of entries in an observable object unless observable.map
is used. Or upgrading to mobx v5
should solve the issue.
For your specific issue you can try:
@observable nodeIdToNodeData = {};
//...
this.nodeIdToNodeData = {...this.nodeIdToNodeData, [currNode.id]: currNode};
Or try to upgrade to mobx v5
.
More info here
Upvotes: 2
Reputation: 193
Looks like Edward solved it, similar to Redux it looks like you have to create a new object with the dictionary rather than modify it. I'm guessing it's because the key currNode.id is already defined (as the value undefined), and we're just modifying it to be currNode. That's my guess, regardless it works now.
Upvotes: 0