Closed tinchoz49 closed 3 years ago
Having the same problem. This is an issue with force-graph.
@tinchoz49 thanks for reaching out.
The issue here is that the value of graphData
changes at every tick of your setInterval
, because you're creating a new object every time. This in turn instructs React that the prop has changed and the inner force graph component should be updated with new data, triggering a re-heat of the force simulation.
The solution is to memoize your data
object so that the reference doesn't change at every render.
This is not an issue with force-graph
but rather how React triggers re-renders based on prop value changes.
@vasturiano The simulation shouldn't "re-heat" even if you set the same data.
There are of course lots of use cases the underlying data changes but the set of nodes and edges remains the same. For example, I may have some underlying data that I use to specify the visual size or colour of each graph node. Those things may change, but shouldn't cause the whole graph to spasm. (Changing the size should of course cause the tree to re-animate, although I would expect this to be tweened and not cause sudden spasms.)
As an aside, the fact that this library uses mutable data doesn't really fit React. It took me a few hours before noticed that the component would add position and velocity to my prop, which feels really wrong to me. That is internal state, and my own data is supposed to be immutable.
@atombender please note that if you pass a new object reference to graphData
at every render, the component does not know that it is the same data and can only assume that it's a new data set. The way to solve this is to memoize the input prop so its value doesn't change. This is a common React pattern.
Regarding why the simulation reheats, this happens when data changes are detected. The rationale behind it is that because the data is different the graph needs to find a new balance and adjust to the changes. The way to do that is to re-heat the simulation. If you find the reheating settings too drastic for your use case, there's multiple parameters you can manipulate to calibrate this to your needs: d3AlphaDecay
, d3VelocityDecay
, d3AlphaMin
, cooldownTicks
, warmupTicks
.
Tweening is not applicable as the simulation ticking is tied into the frame engine, so you have new positions at every frame.
The fact that position and velocity attributes are bound to the node objects is a pattern adopted from d3-force (and in fact does not occur if you're using the ngraph
engine).
Hey @vasturiano, thank you for your help. You right, it checks the same reference of the data object, I thought that by just keeping the same nodes
object reference would be enough that's why I didn't understand then.
For example changing the code like below works as expected:
useEffect(() => {
setInterval(() => {
setData((oldState) => {
return oldState;
});
}, 1000);
}, []);
Describe the bug
I have a graph where I'm just updating the state with the same referenced nodes and during this update where nothing change I noticed miminal moves between the nodes, like a "heartbeat" on every update.
To Reproduce
Try the code below:
Expected behavior If the nodes are the same I would like to have the graph freeze, I mean, without doing minimal moves.
Screenshots
Desktop (please complete the following information):