Skip to content
This repository has been archived by the owner on Jan 30, 2021. It is now read-only.

lifeomic/react-vis-network

Repository files navigation

React Vis Network

Build Status

Installation

yarn install @lifeomic/react-vis-network

Overview

react-vis-network uses a declarative approach to defining vis.js networks. This allows developers to work in React using normal stateful approaches like Redux and have the underlying network chart manage it's own lifecycle.

Example

import React, { Component } from 'react';
import { Network, Node, Edge } from '@lifeomic/react-vis-network';

class MyNetwork extends Component {
  render() {
    return (
      <Network>
        <Node id="vader" label="Darth Vader" />
        <Node id="luke" label="Luke Skywalker" />
        <Node id="leia" label="Leia Organa" />
        <Edge id="1" from="vader" to="luke" />
        <Edge id="2" from="vader" to="leia" />
      </Network>
    );
  }
}

API

< Network />

<Network /> is the top level component that defines your network. All Nodes, Edges and Cluster* components need to be inside a Network to work properly.

Props

options: Object of vis options to configure the look and feel, physics, and interaction patterns in the graph. All options are passed directly to vis and can be overriden on a case by case bases by setting individual options on each Node or Edge

scale: Zoom scale of the network. Defined as a number from 0-1 where 1 is 100% zoomed and 0 is zoomed out infinitely. As returned from network.getScale.

position: Object ({ x, y }) containing coordinates of the central focus point. As returned from network.getViewPosition.

style/className: Normal style and className props to enable styling the network's wrapping element. NB: Overidding position: relative and overflow: hidden will result in wonky node decorator behavior.

< Node />

A Node represents a vis.js network node. As props, a Node accepts all options accepted in normal configuration. It also accepts two special props component and decorator.

component: SVG element to be rendered as the Node in the Network. component is a render prop recieves all of the props passed to Node and returns a react component. The svg will be rendered as the image of the node. Helpful tip, you can pass additional props to node that can be used in your render prop to dynamically change your image.

const CustomIcon = ({ icon, color = '#5596ed' }) => {
  const viewBox = 36;
  const iconSize = 20;
  const pad = (viewBox - iconSize) / 2;
  const center = viewBox / 2;

  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox={`0 0 ${viewBox} ${viewBox}`}
    >
      <g>
        <circle cx={center} cy={center} r={16} fill={color} />
        <g transform={`translate(${pad}, ${pad})`}>
          {React.createElement(icon, { color: 'white', size: iconSize })}
        </g>
      </g>
    </svg>
  );
};

// Use your render prop and custom props passed to `Node` to
// Generate differnt icons per node
const MyNetwork = () => (
  <Network>
    <Node
      id="vader"
      label="Darth Vader"
      component={CustomIcon}
      color="black"
      icon={Sith}
    />
    <Node
      id="luke"
      label="Luke Skywalker"
      component={CustomIcon}
      color="white"
      icon={Jedi}
    />
    <Node
      id="leia"
      label="Leia Organa"
      component={CustomIcon}
      color="gold"
      icon={Princess}
    />
    <Edge id="1" from="vader" to="luke" />
    <Edge id="2" from="vader" to="leia" />
  </Network>
);

NB: This is simply syntactic sugar over using HTML in Nodes and will end up being a data+image. You will not recieve dom events through this component, use Network's event system or decorators instead.

decorator: A react element positioned at the top center of rendered Node. A decorator is similar to component in that it's a render prop that recieves the props of a the node, and returns a react element. This element however doesn't need to be an svg.

const Decorator = props => {
  return (
    <button
      style={decoratorStyles}
      onClick={() => console.log(`You clicked ${props.label}`}
    >
      Click Me
    </button>
  );
};

// Use your render prop and custom props passed to `Node` to
// cause differnet actions in your Decorator
const MyNetwork = () => (
  <Network>
    <Node id='vader' label='Darth Vader' decorator={Decorator} />
    <Node id='luke' label='Luke Skywalker' decorator={Decorator} />
    <Node id='leia' label='Leia Organa' decorator={Decorator} />
    <Edge id='1' from='vader' to='luke' />
    <Edge id='1' from='vader' to='leia' />
  </Network>
);

NB: Because this is a positioned HTML element, it can recieve DOM events like click and input. While fairly performant in positioning and rendering, it can be straining if many nodes have decorators and should be used sparingly. For static (non-interactable) content, it would be better to use a component with a changing prop to re-render it.

< Edge />

An Edge represents a vis.js edge and connects nodes together.

Edges accept all of the options vis exposes for edges as props. One specific note, vis doesn't require that edges have an id, react-vis-network does in order to keep the network updated.

< ClusterByConnection />

A ClusterByConnection is a representation of a vis.js cluster. This particular cluster implementation collapses all nodes around, and including, it's rootNodeId into a cluster.

ClusterByConnection takes all props that Node does including component and decorator.

rootNodeId: id of a Node for which to cluster around.

Performance Considerations

All exported components are Pure Components. It's important to not define anonymous functions and objects inside the render function as it will cause unnecessary re-renders.

// bad, re-renders every time
render () {
  return <Node component={() => <SomeIcon />} />
}

// good, only re-renders when other props change
renderSomeIcon = () => <SomeIcon />

render () {
  return <Node component={this.renderSomeIcon} />
}

Use decorators on Nodes sparingly, if you can do it in the component you should.

Extending, custom clusters, and custom modules

⚠️ Advanced topics, here be dragons.

Want to write a custom Cluster component? Want to add a bit to the lifecycle of a node?

The base of Nodes and Clusters is a Module. Take a peak at the source and create your own class that extends the Module (if you want decorator and compoment support anyway) and use it.

The design of each component is to manage it's lifecycle inside the vis network with react lifecycle methods. Every child inside a Network is passed as props a network (vis.Network), edges (vis.Dataset) and nodes (vis.Dataset).

Helpers

In order to help you roll your own lifecycle methods, the internal converter for component and decorator is exposed as reactToSvgImageUrl.

reactToSvgImageUrl (element) -> String: Accepts a JSX element and returns it rendered as a data:image string.

Accessing the underlying vis network

⚠️ This is an escape hatch, if you find yourself needing to do this, it's likely that the package authors simply haven't thought of your use-case. We would like to! Please open an issue and we can work to build a supported solution.

The Network component has, as instance properties, network (a vis.js network), edges (a vis.js Dataset representing it's edges), and nodes a vis.js Dataset representing it's nodes).

You can access these by creating a ref to the mounted Network.