dunnock / react-sigma

Lightweight React library for drawing network graphs built on top of SigmaJS
https://dunnock.github.io/react-sigma/
MIT License
261 stars 43 forks source link

Pushing new nodes to a graph/updating graph data #16

Open RogerTangos opened 7 years ago

RogerTangos commented 7 years ago

Sorry if this is a noob react question. I'm trying to add and delete nodes and edges from an existing Sigma component. Currently, my component is set up as so:

<Sigma graph={{nodes:[{id:"n1", label:"Alice"}, {id:"n2", label:"Rabbit"}], edges:[{id:"e1",source:"n1",target:"n2",label:"SEES"}]}} settings={{drawEdges:true}}>
   <RelativeSize initialSize={15}/>
   <RandomizeNodePositions/>
</Sigma>

I can reach into the component and change Sigma.props.graph, but have trouble forcing the graph to re-render. How would you recommend doing this?

RogerTangos commented 7 years ago

@dunnock I'm at arcarter@csail.mit.edu if you'd like to correspond. (I did a quick search, but couldn't find your email.)

dunnock commented 7 years ago

Hi @RogerTangos, good spot! It was not possible in the previous version via props update, just fixed so with version 1.2.17 it should be possible.

I suggest to try to update graph data explicitly in JSX or using React.cloneElement, because React.Components.props is readonly from within react component. React reconciles component when rendering, making sure it does not create new component, rather calling componenDidUpdate, where refresh is handled now.

<Sigma graph={updatedGraphData} settings={{drawEdges:true}}>
   <RelativeSize initialSize={15}/>
   <RandomizeNodePositions/>
</Sigma>

Hope this helps!

dunnock commented 7 years ago

Should be fixed with https://github.com/dunnock/react-sigma/commit/5d12186050f716fc511824288fd364b537fe8412

cecampos commented 7 years ago

I still have the same problem. When new graph data comes, the old graph goes away but the new one is not rendered. No error is shown in console.

Any insights? I'm running react-sigma version 1.2.17.

dunnock commented 7 years ago

Hi @cecampos , I had to rollback the fix, since there is no graph merge function in sigma.js library. Therefore any update to the props was cleaning whole graph and setting up new without coordinates. Approach which @RogerTangos has taken appeared to be better - he is making it manageable via adding graph nodes as react components.

jkolbe commented 6 years ago

@dunnock, @RogerTangos could you possible share an example of how you managed to get the re-rendering to work?

RogerTangos commented 6 years ago

Hey @jkolbe and @cecampos, It's been a while since I worked on the project that was using this, so my information may be out of date.

In my graph's render function, I iterate over current nodes and then display them.

When the graph receives new properties, it reinitializes..

The nodes of a graph exist as their own components.

It's not an especially elegant solution, but it did work for our purposes (which were to demo).

A tip: when working with React-Sigma, I found that some combinations of sigma settings caused the graph to not render, or to behave in very strange and unintuitive ways. I'd encourage you to find a few sigmaSettings that work for you, and not change them until everything else with your graph is working.

yonahforst commented 6 years ago

Having the same issue with nodes not updating, and trying to implement @RogerTangos solution. I add the nodes+edges to the graph as react components, but they still dont update when i make changes. Specifically, I'm trying to change the label text. Do I need a hook in the componentWillReceiveProps of the node to call sigma.refresh() or remove+add the node?

dunnock commented 6 years ago

@yonahforst I also would suggest you to try <Sigma settings={{clone: false}} ...>, then each Node to keep a reference to its object, that it has put to sigma.addNode(nodeRef) from within you Node's componentDidMount(). Later in the Node's componentDidUpdate() if you update the nodeRef properties, it should update in the sigma graph. sigma.refresh() might be required though, I would suggest to try without it initially.

yonahforst commented 6 years ago

@dunnock - thanks! this seemed to work:

class SigmaNode extends Component {
  constructor(props){
    super(props);
    this.embedProps = this.embedProps.bind(this);
    this.node = { ...props }
    props.sigma.graph.addNode(this.node)
  }

  componentWillReceiveProps({  label }) {
    Object.assign(this.node, { label })
  }
yonahforst commented 6 years ago

I found a different way that seems to be more performant

class UpdateNodeProps extends React.Component {
  componentWillReceiveProps({ sigma, nodes }) {
    sigma.graph.nodes().forEach(n => {
      var updated = nodes.find(e => e.id == n.id)
      Object.assign(n, updated)
    })
  }

  render = () => null
}

....
<Sigma 
  graph={{ nodes, edges }}
  style={{ flex: 1 }}
  renderer='canvas'
  settings={settings}
  onClickNode={this.onClickNode.bind(this)}
  onClickStage={this.onClickStage.bind(this)}>

  <UpdateNodeProps nodes={nodes}/>

</Sigma>
...
dunnock commented 6 years ago

@yonahforst Great! Would you mind adding pull request with this component in /src and new small section in Readme.md? Mutable graph nodes

matthewberryman commented 5 years ago

In my case I was only wanting to update the graph once, after load of data (I couldn't use LoadJSON as I needed to generate an AWS4 signed API call). I worked around this issue a little bit more simply by making the render a ternary based on state, so on the first call (when data isn't loaded) it doesn't even render the sigma, then after that it does once and no need for the above solutions for further updates in my use case. Code here: https://github.com/AcrossTheCloud/TBA21-client/blob/5c33303e436c71832c90f0e91f89fd203808ba1b/src/components/NetworkGraph.tsx I like @yonahforst's approach above for a more general solution and I will look into making a PR if I get a little free time over the Xmas break.

jvansan commented 3 years ago

I have found the solution by @yonahforst to work really well.

In my case, I wanted to update the colors and labels of nodes. I found that I had to add an extra line to force sigma to redraw. I know this is documented in the sigma repo, but figured this might be helpful to anyone struggling to get their node props to render immediately.

class UpdateNodeProps extends React.Component {
  componentWillReceiveProps({ sigma, nodes }) {
    sigma.graph.nodes().forEach((n) => {
      var updated = nodes.find((e) => e.id == n.id)
      Object.assign(n, updated)
    })
    // Tell sigma to redraw
    sigma.refresh()
  }

  render = () => null
}
michaelbdavid commented 2 years ago

Is there a functional component way of doing this? I attempted to convert the above but it's unclear to me how I can pass the sigma object to my FC as I do not have the legacy React component experience. Any help is appreciated.


const UpdateNodeProps = ({ sigma, nodes }: { sigma: any; nodes: any[] }) => {
    useEffect(() => {
        sigma.graph.nodes().forEach((n: any) => {
            const updated = nodes.find((e) => e.id === n.id);
            Object.assign(n, updated);
        });
        // Tell sigma to redraw
        sigma.refresh();
    }, [nodes, sigma, sigma.graph.nodes]);

    return null;
};

export default UpdateNodeProps;
-------
const sigma = useRef();
<Sigma 
 ref={sigma}
  graph={{ nodes, edges }}
  style={{ flex: 1 }}
  renderer='canvas'
  settings={settings}
  onClickNode={this.onClickNode.bind(this)}
  onClickStage={this.onClickStage.bind(this)}>

  <UpdateNodeProps sigma={sigma} nodes={nodes}/>

</Sigma>

Update on this. Adding ref to Sigma and passing to UpdatedNodeProps seems to work :)

michaelbdavid commented 2 years ago

So now that I'm able to update/add new nodes and edges via the above. How can i force nodes to layout again via ForceAtlas2 or Randomize? Right now it seems whatever gets added keeps the original x,y set on the node. I'd like to relayout when new nodes/edges have been added.

michaelbdavid commented 2 years ago

Honestly it would be nice just to have Sigma updated when the graph data updates and then have it relayout based on that data. Is that the plan or is this just the way react-sigma will continue to work?