React Flow教程:轻松构建交互式节点图

React Flow 教程:轻松构建交互式节点图

在当今的 Web 开发领域,可视化数据和流程变得越来越重要。无论是流程图、组织结构图、还是网络拓扑图,都需要一种直观且交互的方式来呈现。React Flow 是一个强大的 React 库,专门用于构建基于节点和边的交互式图表。它提供了丰富的功能和高度的可定制性,使开发者能够轻松创建各种复杂的图表应用。

本教程将带你深入了解 React Flow 的核心概念、基本用法以及高级特性,并通过实际示例演示如何构建一个功能完备的节点图应用。无论你是 React 初学者还是有经验的开发者,都可以通过本教程掌握 React Flow 的使用技巧。

1. React Flow 简介

React Flow 是一个开源的 React 库,用于构建基于节点和边的交互式图表。它提供了以下核心功能:

  • 节点 (Nodes):可拖动、可连接的基本单元,可以自定义外观和行为。
  • 边 (Edges):连接节点的线条,可以自定义样式和交互。
  • 缩放和平移 (Zoom & Pan):内置的缩放和平移功能,方便用户浏览大型图表。
  • 迷你地图 (Minimap):提供图表的概览,方便用户快速导航。
  • 控件 (Controls):提供缩放、适应视图等常用操作按钮。
  • 可定制性 (Customizability):高度可定制的节点、边、样式和交互。
  • 事件系统 (Event System):提供丰富的事件,方便开发者处理用户交互和自定义行为。
  • 支持 TypeScript:提供完整的 TypeScript 类型定义,方便开发者使用。

React Flow 的优势在于:

  • 易于使用:API 简洁直观,易于上手。
  • 性能优异:基于 React 构建,性能高效。
  • 功能强大:提供丰富的功能和高度的可定制性。
  • 社区活跃:拥有活跃的社区和完善的文档。

2. 安装和基本用法

2.1 安装

使用 npm 或 yarn 安装 React Flow:

```bash
npm install reactflow

yarn add reactflow
```

2.2 基本用法

以下是一个简单的 React Flow 示例,创建了两个节点和一条边:

```jsx
import React from 'react';
import ReactFlow, { Controls, Background } from 'reactflow';
import 'reactflow/dist/style.css';

const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'Node 1' },
position: { x: 250, y: 5 },
},
{
id: '2',
data: { label: 'Node 2' },
position: { x: 100, y: 100 },
},
];

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

const MyFlow = () => {
return (




);
};

export default MyFlow;
```

代码解释:

  • import ReactFlow, { Controls, Background } from 'reactflow';:导入 React Flow 组件。
  • 'reactflow/dist/style.css';:导入 React Flow 样式。
  • initialNodes:定义初始节点数组。
    • id:节点的唯一标识符。
    • type:节点类型(可选,用于自定义节点)。
    • data:节点数据(可以自定义任何数据)。
    • position:节点位置。
  • initialEdges:定义初始边数组。
    • id:边的唯一标识符。
    • source:源节点 ID。
    • target:目标节点 ID。
  • <ReactFlow>:React Flow 根组件。
    • nodes:传入节点数组。
    • edges:传入边数组。
    • <Controls />: 缩放等控制组件
    • <Background />: 背景组件
  • <div style={{ width: '100vw', height: '100vh' }}>: 设置画布大小.

这个示例创建了一个包含两个节点和一条边的简单图表。你可以拖动节点、缩放和平移视图。

3. 节点和边

3.1 节点类型

React Flow 内置了几种节点类型:

  • input:输入节点,通常作为流程的起点。
  • output:输出节点,通常作为流程的终点。
  • default:默认节点。

你可以通过 type 属性指定节点类型:

jsx
{
id: '1',
type: 'input',
data: { label: 'Input Node' },
position: { x: 250, y: 5 },
}

3.2 自定义节点

你可以创建自定义节点类型,以满足特定的需求。自定义节点需要创建一个 React 组件,并将其注册到 React Flow 中。

示例:创建一个自定义节点

```jsx
// CustomNode.js
import React from 'react';
import { Handle, Position } from 'reactflow';

const CustomNode = ({ data }) => {
return (

{data.label}


);
};

export default CustomNode;
```

代码解释:

  • <Handle>:连接点组件。
    • type:连接点类型,source 表示可以作为边的起点,target 表示可以作为边的终点。
    • position:连接点位置,可以是 topbottomleftright
  • data: 从 ReactFlow 传入的节点数据。

注册自定义节点

```jsx
// App.js
import ReactFlow, { Controls } from 'reactflow';
import CustomNode from './CustomNode';

const nodeTypes = {
custom: CustomNode,
};

const MyFlow = () => {
const initialNodes = [
{
id: '1',
type: 'custom', // 使用自定义节点类型
data: { label: 'Custom Node', color: 'lightblue' },
position: { x: 100, y: 100 },
},
];
return (



);
};
```

代码解释:

  • nodeTypes:一个对象,将自定义节点类型名称映射到 React 组件。
  • <ReactFlow nodeTypes={nodeTypes}>:将 nodeTypes 传递给 ReactFlow 组件。

现在,你可以在 initialNodes 中使用 type: 'custom' 来创建自定义节点。

3.3 边类型

React Flow 内置了几种边类型:

  • default:默认边,直线。
  • straight:直线边。
  • step:阶梯线边。
  • smoothstep:平滑阶梯线边。
  • bezier: 贝塞尔曲线

你可以通过 type 属性指定边类型:

jsx
{
id: 'e1-2',
source: '1',
target: '2',
type: 'smoothstep',
}

3.4 自定义边

与自定义节点类似,你也可以创建自定义边。

示例:创建一个自定义边

```jsx
// CustomEdge.js
import React from 'react';
import { getBezierPath, getEdgeCenter } from 'reactflow';

const CustomEdge = ({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style = {},
data,
}) => {
const [edgePath, labelX, labelY] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});

return (
<>

#${id}}
style={{ fontSize: '12px' }}
startOffset="50%"
textAnchor="middle"
>
{data.text}



);
};

export default CustomEdge;
```

注册自定义边
```javascript
// App.js
import ReactFlow, { Controls } from 'reactflow';
import CustomEdge from './CustomEdge';

const edgeTypes = {
custom: CustomEdge,
};

const MyFlow = () => {
const initialEdges = [
{
id: 'e1-2',
source: '1',
target: '2',
type: 'custom', //使用自定义边
data: { text: 'Custom Edge' },
}
];
return (



);
};
```

4. 事件处理

React Flow 提供了丰富的事件,方便开发者处理用户交互和自定义行为。

4.1 常用事件

  • onNodeClick:节点点击事件。
  • onEdgeClick:边点击事件。
  • onNodeDragStart:节点拖动开始事件。
  • onNodeDrag:节点拖动事件。
  • onNodeDragStop:节点拖动结束事件。
  • onConnect:连接建立事件。
  • onConnectStart: 连接线开始拖动
  • onConnectEnd:连接线拖动结束。
  • onNodesChange:节点更改时触发
  • onEdgesChange: 边更改时触发

4.2 事件处理示例

```jsx
import React, { useCallback } from 'react';
import ReactFlow, {
addEdge,
applyNodeChanges,
applyEdgeChanges,
} from 'reactflow';

const MyFlow = () => {
const [nodes, setNodes] = React.useState([]);
const [edges, setEdges] = React.useState([]);

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

return (

);
};
```

代码解释:

  • onNodesChange: 当节点删除、移动等操作导致节点数据发生变化时触发。
  • onEdgesChange: 当边删除等操作导致边数据发生变化时触发。
  • applyNodeChanges, applyEdgeChanges: React Flow提供的帮助函数,用于更新节点、边数据。
  • onConnect:当用户创建新的连接时触发。
  • addEdge: React Flow 提供的帮助函数, 用于添加新的边。

5. 高级特性

5.1 迷你地图 (Minimap)

迷你地图提供图表的概览,方便用户快速导航。

```jsx
import ReactFlow, { Minimap } from 'reactflow';

const MyFlow = () => {
return (



);
};
```

5.2 背景 (Background)

可以为图表添加背景。

```jsx
import ReactFlow, { Background } from 'reactflow';

const MyFlow = () => {
return (



);
};
``variant属性可以设置为dots,lines,cross`

5.3 useReactFlow Hook

useReactFlow Hook 提供了对 ReactFlow 实例的方法的访问,例如 fitView, zoomTo, getNodes等。

```jsx
import { useReactFlow } from 'reactflow';

const MyComponent = () => {
const { fitView, zoomTo, getNodes } = useReactFlow();

const focusNode = () => {
    const nodes = getNodes();
    if(nodes.length > 0){
         fitView({ nodes: [nodes[0]], padding: 0.2 });
         zoomTo(2);
    }
}

return <button onClick={focusNode}>Focus Node</button>

}

```

5.4 状态管理

当构建复杂的图表应用时,通常需要使用状态管理库(如 Redux、Zustand)来管理节点和边的数据。

以下是使用 Zustand 的示例:

```javascript
// store.js
import { create } from 'zustand'

const useStore = create((set) => ({
nodes: [],
edges: [],
onNodesChange: (changes) => {
set({
nodes: applyNodeChanges(changes, get().nodes),
});
},
onEdgesChange: (changes) => {
set({
edges: applyEdgeChanges(changes, get().edges),
});
},
onConnect: (connection) => {
set({
edges: addEdge(connection, get().edges),
});
},
}));

export default useStore;
```

```javascript
// App.js
import ReactFlow from 'reactflow';
import useStore from './store';
const MyFlow = () => {
const nodes = useStore((state) => state.nodes);
const edges = useStore((state) => state.edges);
const onNodesChange = useStore((state) => state.onNodesChange);
const onEdgesChange = useStore((state) => state.onEdgesChange);
const onConnect = useStore((state) => state.onConnect);

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

}

```

6. 完整示例:流程图编辑器

下面是一个简单的流程图编辑器的示例,演示了 React Flow 的一些常用功能:

```jsx
import React, { useState, useCallback, useRef } from 'react';
import ReactFlow, {
ReactFlowProvider,
addEdge,
useNodesState,
useEdgesState,
Controls,
Background,
} from 'reactflow';
import 'reactflow/dist/style.css';

import Sidebar from './Sidebar';

const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'Start' },
position: { x: 250, y: 5 },
},
{
id: '2',
data: { label: 'Middle' },
position: { x: 100, y: 100 },
},
{
id: '3',
type: 'output',
data: { label: 'End' },
position: { x: 250, y: 200 },
},
];

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

let id = 0;
const getId = () => dndnode_${id++};

const MyFlow = () => {
const reactFlowWrapper = useRef(null);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const [reactFlowInstance, setReactFlowInstance] = useState(null);

const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []);

const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);

const onDrop = useCallback(
(event) => {
event.preventDefault();

  const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
  const type = event.dataTransfer.getData('application/reactflow');

  // check if the dropped element is valid
  if (typeof type === 'undefined' || !type) {
    return;
  }

  const position = reactFlowInstance.project({
    x: event.clientX - reactFlowBounds.left,
    y: event.clientY - reactFlowBounds.top,
  });
  const newNode = {
    id: getId(),
    type,
    position,
    data: { label: `${type} node` },
  };

  setNodes((nds) => nds.concat(newNode));
},
[reactFlowInstance]

);

return (





);
};

export default MyFlow;

```

```jsx
// Sidebar.js
import React from 'react';

const Sidebar = () => {
const onDragStart = (event, nodeType) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};

return (

);
};
export default Sidebar;
```

```css
/ App.css or index.css /
.dndflow {
display: flex;
width: 100%;
height: 100vh; / 重要:确保高度充满视口 /
}

.reactflow-wrapper {
flex-grow: 1; / 让 ReactFlow 填充剩余空间 /
height: 100%; / 非常重要: 必须设置高度 /
width: 100%;
}
aside {
width: 200px;
border-right: 1px solid #eee;
padding: 15px;
font-size: 12px;
background: #fcfcfc;
}

aside .description {
margin-bottom: 10px;
}

.dndnode {
height: 20px;
padding: 4px;
border: 1px solid #1a192b;
border-radius: 2px;
margin-bottom: 10px;
display: flex;
justify-content: center;
align-items: center;
cursor: grab;
}

.dndnode.input {
border-color: #0041d0;
}

.dndnode.output {
border-color: #ff0072;
}

```

代码解释:

  • <ReactFlowProvider>:React Flow 的上下文提供者,用于在组件树中共享 React Flow 实例。
  • useNodesStateuseEdgesState:React Flow 提供的 Hook,用于管理节点和边的状态。
  • onDragOveronDrop:处理拖放事件,实现从侧边栏拖动节点到画布的功能。
  • reactFlowInstance.project:将屏幕坐标转换为画布坐标。
  • Sidebar 组件:包含可拖动的节点。

这个示例实现了一个简单的流程图编辑器,你可以从侧边栏拖动节点到画布,并连接它们。

7. 总结

React Flow 是一个功能强大且易于使用的 React 库,用于构建交互式节点图。本教程介绍了 React Flow 的核心概念、基本用法、高级特性以及一个完整的示例。通过本教程,你应该已经掌握了 React Flow 的基本使用技巧,并能够构建自己的节点图应用。

React Flow 的功能远不止于此,还有更多高级特性和定制选项等待你去探索。建议你阅读 React Flow 的官方文档,了解更多详细信息和示例。

THE END