Search code examples
reactjsgraph

Building a unidirectional graph in React


I have a use case where I need to build a unidirectional graph, which basically explains the dependency of the child node to it's parent node and so on....

For a corresponding input(one root node), multiple child nodes and sub child nodes need to be rendered on the screen in the form of a graph

Any suggestions to which library I can use to fulfil this.

I dont know how many children would be there for each root node, so giving specific coordinates in the 2d space would be quite cumbersome. I'll be getting the input data in the form of a json for which I need to build the graph

Example

enter image description here

Flowpoints library (flowpoints npm) is the one I saw until now which can do something like this, but if anyone has a better suggestion


Solution

  • Use React-Flow with dagre. Dagre automatically positions nodes for the graph. Sample code below. Also check out the website https://reactflow.dev/docs/

        import React, { useState, useCallback } from 'react';
        import ReactFlow, {
        ReactFlowProvider,
        addEdge,
        removeElements,
        isNode,
        } from 'react-flow-renderer';
        import dagre from 'dagre';
    
    
        class LayoutFlow extends React.Component{
          constructor(props){
            super(props)
            this.state = {
              elements : []
            }
          }
        
          componentDidMount(){
            const position = { x: 0, y: 0 };
            const edgeType = 'smoothstep';
            const array = [{id:"1",data:"node 1",output:["2","3"]},{id:"2",data:"node 2",output:["2a","2b","2c"]},{id:"3",data:"node 3"},
            {id:"2a",data:"node 2a"},{id:"2b",data:"node 2b"},{id:"2c",data:"node 2c",output:["2d"]},{id:"2d",data:"node 2d"}
          ]
            const newArray = []
            const k = 0
            array.map((ele,i) => {
              newArray.push({id:ele.id,data:{label:<div><h3>{ele.data}</h3><h4>This is a node</h4></div>},position, connectable:false})
            })
            array.map((ele,i) => {
              if(ele.output !== undefined && ele.output !== null){
                ele.output.map((element,p) => {
                  newArray.push({id:"e"+ele.id+element,source:ele.id,target:element,type: edgeType})
                })
              }
            })
            this.setState({ elements : newArray}, () =>{
        
              const dagreGraph = new dagre.graphlib.Graph();
        dagreGraph.setDefaultEdgeLabel(() => ({}));
        const getLayoutedElements = (elements, direction = 'TB') => {
          const isHorizontal = direction === 'LR';
          dagreGraph.setGraph({ rankdir: direction });
          elements.forEach((el) => {
            if (isNode(el)) {
              dagreGraph.setNode(el.id, { width: 300, height: 150 });
            } else {
              dagreGraph.setEdge(el.source, el.target);
            }
          });
          dagre.layout(dagreGraph);
          return elements.map((el) => {
            if (isNode(el)) {
              const nodeWithPosition = dagreGraph.node(el.id);
              el.targetPosition = isHorizontal ? 'left' : 'top';
              el.sourcePosition = isHorizontal ? 'right' : 'bottom';
              el.position = {
                x: nodeWithPosition.x + Math.random() / 1000,
                y: nodeWithPosition.y,
              };
            }
            return el;
          });
        };
        const layoutedElements = getLayoutedElements(this.state.elements);
        
            })
          }
        
          onConnect = (params) =>{
            this.setState((els) =>
            addEdge({ ...params, type: 'smoothstep'}, els))
          }
        
          render(){
            return (
              <div className="layoutflow" style={{width:10000,height:10000}}>
                <ReactFlowProvider>
                  <ReactFlow
                    elements={this.state.elements}
                    onConnect={this.onConnect}
                    connectionLineType="smoothstep"
                  />
                </ReactFlowProvider>
              </div>
            );
          }
        
        }
        export default LayoutFlow;