I’m trying to use go.js with react and redux. I want to have my graph model stored in redux store and I’d like to be able to change this model from outside of go.js (for example someone clicks a button on a page, react/redux updates store and as a result graph also gets updated).
I use diagram.model.addChangedListener to trigger updates in store whenever something gets changed directly in gojs (say, user resized a node). This works as I’d like it to. I dispatch an action, reducer gets called with current model which gets stored in redux store. The problem is that as soon as I change something from outside of gojs itself my UndoManager stops working correctly.
Basically what react does is calling a method every time data connected to a part of DOM gets changed. In my case this method looks like this (this.props.nodes contains latest state of nodes from redux store):
componentDidUpdate(prevProps, prevState, prevContext) {
if (this.props.nodes !== prevProps.nodes) {
let diagram = this.state.myDiagram;
diagram.model.nodeDataArray = this.props.nodes;
}
}
If a node gets changed in redux store because someone changed something in the application (clicked a button for example) this method will be called and nodeDataArray will be overriden. As soon as it happens ctrl+z stops working (when I call it, everythin disappears).
Is there a way to get this flow working? I mean, I probably could iterate previous nodes from prevState, compare them to this.props.nodes (new state) and try to only update changed nodes. This is - however - quite complicated and I’d rather avoid it if possible.
I am able to provide a full example if needed (in typescript), but it is quite big (more than 100 lines of code) so I don’t want to put it here if it is not needed.
By replacing the Model.nodeDataArray, you are deleting all existing Nodes and creating all new Nodes. This is why undo causes all of the nodes to disappear. (But I don’t know why the original ones do not re-appear – perhaps you enabled the UndoManager after setting up the initial model.)
My point is that you could send JSON objects in the form that Model.applyIncrementalJson can accept, since it uses keys/ids to refer to node data or link data.
I haven’t tried this, but maybe something like:
diagram.model.applyIncrementalJson({
class: "go.GraphLinksModel",
incremental: 1,
nodeKeyProperty: "key", // or whatever you are using
linkKeyProperty: "key", // or whatever you are using
modifiedNodeData: this.props.nodes,
modifiedLinkData: this.props.links // I assume you have an Array of link data too
});
thanks for suggestion. I’ll definitely take a look at incrementalJson.
As for a problem with disappearing nodes. UndoManager is definitely enabled from the beginning. My app starts and I can move/resize nodes as much as I want and ctrl+z will work as expected. Only when I call componentDidUpdate mentioned above it stops working. Funny thing is that actual transactions (or changes) are still in UndoManager. If I call ctrl+z several times and then start calling redo(ctrl+y) the changes will be performed again and nodes would start reappearing as expected. Also when I check diagram.undoManager.history.length it is correct.
I know that what I’m trying to do here is a brute force approach (replacing nodes array every time something is changing). Problem is that trying to synchronize nodes array in graph and redux store based on individual changes is quite complicated. For simplicity I’d rather replace it entirely (at least for now - as my graphs are not that complicated).
Well, if you call Model.applyIncrementalJson, you’ll have the flexibility of passing all of the node data and all of the link data, or any subset of it.
I didn’t mention it above, but if you want to add or remove nodes or links, you’ll need to provide that information in Arrays that are additional properties. You’ll see if it you add or remove nodes or links in that State Chart Incremental sample.