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 (
);
};
export default CustomNode;
```
代码解释:
<Handle>
:连接点组件。type
:连接点类型,source
表示可以作为边的起点,target
表示可以作为边的终点。position
:连接点位置,可以是top
、bottom
、left
、right
。
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 (
<>
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 实例。useNodesState
和useEdgesState
:React Flow 提供的 Hook,用于管理节点和边的状态。onDragOver
和onDrop
:处理拖放事件,实现从侧边栏拖动节点到画布的功能。reactFlowInstance.project
:将屏幕坐标转换为画布坐标。Sidebar
组件:包含可拖动的节点。
这个示例实现了一个简单的流程图编辑器,你可以从侧边栏拖动节点到画布,并连接它们。
7. 总结
React Flow 是一个功能强大且易于使用的 React 库,用于构建交互式节点图。本教程介绍了 React Flow 的核心概念、基本用法、高级特性以及一个完整的示例。通过本教程,你应该已经掌握了 React Flow 的基本使用技巧,并能够构建自己的节点图应用。
React Flow 的功能远不止于此,还有更多高级特性和定制选项等待你去探索。建议你阅读 React Flow 的官方文档,了解更多详细信息和示例。