uber/react-vis-force

Question: how to manually restart the simulation?

xiaosansiji opened this issue · 3 comments

What I do?

I render the force graph by some node infos;
I add a node to the graph.

What I want?

The graph restart the simulation and render automatically.

What's problem?

The graph render automatically after I add a new node. But the simulation dosen't restart automatically.
so all nodes are pressed together.

How to manually restart the simulation?

I came here to ask the exact same question as @xiaosansiji . I've got the below code example that deletes a node on click, changing the graph like when @xiaosansiji adds a node. On click the node is deleted but all the remaining nodes bunch up in the top left corner.

class App extends Component {
    constructor(props) {
        super(props);
        var links = [
                {"source": "first-node", "target": "second-node"},
                {"source": "third-node", "target": "second-node"},
        ]
        var tasks = [
            {"id": "first-node", "fill": "red"},
            {"id": "second-node", "fill": "blue"},
            {"id": "third-node", "fill": "blue"},
        ]
        this.state = {
            tasks: tasks,
            links: links
        }
    }
    render() {
            function md(r, task) {
                this.setState((prevState) => ({

                    tasks: prevState.tasks.filter((t) => t !== task),
                    links: prevState.links.filter((l) => l.source !== task.id && l.target !== task.id)

                }));
            }
            var md = md.bind(this)
            var tasks = this.state.tasks.map((task) => {
                    return (<ForceGraphNode key={task.id} node={{ id: task.id }} fill={task.fill} onMouseDown={(r) => md(r, task)} />)
            });
            var links = this.state.links.map((link) => {
                    return (<ForceGraphLink key={link.source + '..' + link.target} link={link} />)
            });
            return (<ForceGraph simulationOptions={{ height: 300, width: 300 }}>
                      {tasks}
                      {links}
                    </ForceGraph>)
    }
}

super ghetto workaround with a dependency on lodash.isEqual:

class FG extends ForceGraph {
        componentDidUpdate(prevProps, prevState) {
                var prevNodes = prevProps.children[0].map((c) => c.key);
                var prevLinks = prevProps.children[1].map((c) => c.key);
                var nodes = this.props.children[0].map((c) => c.key);
                var links = this.props.children[1].map((c) => c.key);
                if (!(isEqual(prevNodes, nodes) && isEqual(prevLinks, links))) {
                  console.log("diff!")
                  this.simulation.alpha(1).restart();
                }
        }
}

the restart however results in my componentDidUpdate being called repeatedly for about 5 seconds so there must be something in the restart that's kicking the react update machinery, this isn't an ideal solution.

The reason the simulation does not update is that the alpha value is not being refreshed.

Currently the simulation only ticks as long as simulation.alpha > simulation.alphaMin. While ticking this value is decaying which means that after the first run the simulation will never run again until a new alpha value is set.

The simplest way for now is to pass alpha in simulationsOptions. This will result in an actual restart of the simulation. But in my opinion runSimulation() in the provided utils.updateSimulation() should also reset the alpha value.