1 1_concepts
Jason Zhu edited this page 2023-05-24 22:17:49 +10:00

Concepts

Introduction

React Flow is a library for building node-based applications.

We can use it to build

  • simple static diagrams or
  • data visualisations or
  • complex visual editors.

We can implement

  • custom node types
  • custom edges

The library has builtin minimap and viewport controls

Built-in plugins in React Flow

  • <Background />: implement some basic customisable background patterns
  • <MiniMap />: displays a small version of the graph in the corner
  • <Controls />: adds controls to zoon, center, and lock viewport
  • <Panel />: help to position content on top of viewport
  • <NodeToolbar />: render a toolbar attached to a node
  • <NodeResizer />: add resize funtionality to nodes

Terms and Definitions

3 key components in React Flow are:

  • nodes
  • edges
  • handles

Nodes

node in react flow

  • A node in React Flow is a React component.
  • Each node has a x/y-coordinate, which declare where it's placed in the viewport
  • Details of node options are available in: API: Nodes -> Node Options
  • We can create custom nodes, to create better rendering effect, font etc.

Handles

handles in react flow

  • A handle (also called "port" in other libraries) is the place where an edge (connection) attaches to.
  • Handles can be placed anywhere
  • Default, a handle appears as a grey circle on border (top, bottom, left or right) of the node
  • We can create as many as handles as we want

Edges

edges

  • An edge connects two nodes.
  • Every edge needs a target and a source node.
  • React Flow has 4 built-in edge types
    • default
    • smoothstep
    • step
    • straight
  • Edge is SVG path that can be styled with CSS and customizable
  • If a node has multiple handle, multiple edge can be created

Connection Line

React flow has built-in function to click-and-drag from one handle to another. The placeholder edge during dragging is called connection line. We can configure them according to props section

Viewport

viewport 1

All of React Flow exist in the viewport (screen). Viewport has three values:

  • x
  • y
  • zoom

User interact with viewport (move and magnify) by modifying these three values.

e.g.

  • initial viewport as shown above. Where x=0, y=0, and zoom=1 and
  • changed viewport as shown below. Where x=51.94, y=-33.42, and zoom=1.73

viewport 2

Core Concepts

Learning objectives:

  • Core concepts of React Flow
  • How to create an interactive flow

What is a Flow:

  • Flow consists of nodes or/and edges.

How to pass data into React Flow to render:

  • pass arrays of nodes or/and edges as props to <ReactFlow /> component

Property of node/edge:

  • All node and edge ids need to be unique
  • A node needs a position and a label
  • An edge needs a source (node id) and a target (node id)
  • Details of options are availabe in Node options and Edge options

Controlled or Uncontrolled

React Flow has two way of setup a flow

  • controlled: you are in control of the state of the nodes and edge
  • uncontrolled: nodes and edges are handled by React Flow internally. Details in Uncontrolled Flow
import { useState } from 'react';
import ReactFlow from 'reactflow';
import 'reactflow/dist/style.css';

const initialNodes = [
  {
    id: '1',
    type: 'input',
    data: { label: 'Input Node' },
    position: { x: 250, y: 25 },
  },

  {
    id: '2',
    // you can also pass a React component as a label
    data: { label: <div>Default Node</div> },
    position: { x: 100, y: 125 },
  },
  {
    id: '3',
    type: 'output',
    data: { label: 'Output Node' },
    position: { x: 250, y: 250 },
  },
];

const initialEdges = [
  { id: 'e1-2', source: '1', target: '2' },
  { id: 'e2-3', source: '2', target: '3', animated: true },
];

function Flow() {
  const [nodes, setNodes] = useState(initialNodes);
  const [edges, setEdges] = useState(initialEdges);

  return <ReactFlow nodes={nodes} edges={edges} fitView />;
}

export default Flow;

core concepts -> controlled flow

Basic Functionality

Code in previous section cannot do any select, drag or remove action, as default React Flow doesn't do any internal state update. It only handle viewport when a controlled flow is setup.

To select, drag and remove nodes and edges, we need implement onNodesChange and onEdgeChange handler of <ReactFlow /> component

import { useCallback, useState } from 'react';
import ReactFlow, { applyEdgeChanges, applyNodeChanges } from 'reactflow';
import 'reactflow/dist/style.css';

import initialNodes from './nodes.js';
import initialEdges from './edges.js';

function Flow() {
  const [nodes, setNodes] = useState(initialNodes);
  const [edges, setEdges] = useState(initialEdges);

  const onNodesChange = useCallback(
    (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
    [setNodes]
  );
  const onEdgesChange = useCallback(
    (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
    [setEdges]
  );

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange} // handle node change
      onEdgesChange={onEdgesChange}
      fitView
    />
  );
}

export default Flow;

Where:

  • When <ReactFlow /> trigger a change (node init, node drag, edge select), the onNodesChange handler gets called.
  • applyNodeChanges handler is provided by library (using zustand for internal state management), so it's nearly out of box.
    • applyNodeChanges handler returns an updated array of nodes that used by <ReactFlow /> component

With onNodesChange and onEdgesChange handler, we can have an interactive flow chart with following interactions:

  • selectable nodes and edges
  • draggable nodes
  • removable nodes and edges (deleteKeyCode prop allow remove node via Backspace)
  • multi-selection area by pressing Shift
  • multi-selection by pressing command

To simplify code above, we can directly use hook

const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

Connecting Nodes

Final pazzle of interactive <ReactFlow /> component is onConnect handler. It can be used to handle new connections between nodes

...
import ReactFlow, { addEdge, applyEdgeChanges, applyNodeChanges } from 'reactflow';
...

function Flow() {
  const [nodes, setNodes] = useState(initialNodes);
  const [edges, setEdges] = useState(initialEdges);

  const onNodesChange = useCallback(
    ...
  );
  const onEdgesChange = useCallback(
    ...
  );
  const onConnect = useCallback(
    (connection) => setEdges((eds) => addEdge(connection, eds)),
    [setEdges]
  );

  return (
    <ReactFlow
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
      fitView
    />
  );
}

export default Flow;

Plugin Components

MiniMap

To have overview, we can use built-in MiniMap component, and add it as children in ReactFlow component

import ReactFlow, { MiniMap } from 'reactflow';
import 'reactflow/dist/style.css';

import defaultNodes from './nodes.js';
import defaultEdges from './edges.js';

const nodeColor = (node) => {
  switch (node.type) {
    case 'input':
      return '#6ede87';
    case 'output':
      return '#6865A5';
    default:
      return '#ff0072';
  }
};

function Flow() {
  return (
    <ReactFlow defaultNodes={defaultNodes} defaultEdges={defaultEdges} fitView>
      <MiniMap nodeColor={nodeColor} nodeStrokeWidth={3} zoomable pannable />
    </ReactFlow>
  );
}

export default Flow;

concepts -> plugin components -> minimap

Details of <MiniMap /> component is in API page

Background

<Background /> component can be used to display pattern background

import { useState } from 'react';
import ReactFlow, { Background, Panel } from 'reactflow';
import 'reactflow/dist/style.css';

import defaultNodes from './nodes.js';
import defaultEdges from './edges.js';

function Flow() {
  const [variant, setVariant] = useState('cross');

  return (
    <ReactFlow defaultNodes={defaultNodes} defaultEdges={defaultEdges} fitView>
      <Background color="#ccc" variant={variant} />
      <Panel>
        <div>variant:</div>
        <button onClick={() => setVariant('dots')}>dots</button>
        <button onClick={() => setVariant('lines')}>lines</button>
        <button onClick={() => setVariant('cross')}>cross</button>
      </Panel>
    </ReactFlow>
  );
}

export default Flow;

concepts -> plugin components -> background

Panel

<Panel /> component is helper component that can be used to display content on top of React Flow viewport

import ReactFlow, { Background, Panel } from 'reactflow';
import 'reactflow/dist/style.css';

import './style.css';

const nodes = [
  {
    id: '1',
    data: { label: 'this is an example flow for the <Panel /> component' },
    position: { x: 0, y: 0 },
  },
];

function Flow() {
  return (
    <ReactFlow nodes={nodes} fitView>
      <Panel position="top-left">top-left</Panel>
      <Panel position="top-center">top-center</Panel>
      <Panel position="top-right">top-right</Panel>
      <Panel position="bottom-left">bottom-left</Panel>
      <Panel position="bottom-center">bottom-center</Panel>
      <Panel position="bottom-right">bottom-right</Panel>
      <Background variant="cross" />
    </ReactFlow>
  );
}

export default Flow;

concepts -> plugin components -> panel

Packages

React Flow packages available on npm