🧠 一句话讲清 React Flow 是什么?
👉 React Flow 本质就是一个:
"帮你画流程图的 UI + 帮你收集用户操作的事件系统"
你可以把它想象成:
- ReactFlow → 画布(Canvas)
- nodes → 方块(节点)
- edges → 连线
- 用户操作 → 拖动 / 连接 / 删除
🏗 我们先看最核心代码👇
TypeScript
import { useState, useCallback } from 'react';
import { ReactFlow, applyNodeChanges, applyEdgeChanges, addEdge } from '@xyflow/react';
const initialNodes = [
{ id: 'n1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
{ id: 'n2', position: { x: 0, y: 100 }, data: { label: 'Node 2' } },
];
const initialEdges = [{ id: 'n1-n2', source: 'n1', target: 'n2' }];
👉 nodes 是什么?
👉 就是节点数据源
javascript
{
id: 'n1',
position: { x: 0, y: 0 },
data: { label: 'Node 1' }
}
你可以理解为:
👉 "画布上每一个方块的描述信息"
👉 edges 是什么?
👉 就是连线数据源
javascript
{
id: 'n1-n2',
source: 'n1',
target: 'n2'
}
👉 表示:n1 → n2 有一条线
👉 ReactFlow 在干嘛?
TypeScript
<ReactFlow nodes={nodes} edges={edges} />
👉 它只是:
👉 把你的数据渲染成 UI
⚠️ 重点来了:
👉 React Flow 自己不存数据!
🧠 核心认知(非常重要)
👉 React Flow = UI 渲染器
👉 nodes / edges = 你的数据
👉 onXXXChange = 数据变化入口
一句话总结:
👉 React Flow 是"无状态"的,一切由你控制
🔥 用户操作时到底发生了什么?
我们来看一个真实场景👇
🎬 场景:你拖动了一个节点
发生了什么?
🧩 Step 1:ReactFlow 捕获操作
👉 你拖动节点
🧩 Step 2:生成 change(关键!)
ReactFlow 会生成:
javascript
{
type: 'position',
id: 'n1',
position: { x: 100, y: 200 }
}
👉 这就是"变化描述"
🧩 Step 3:触发 onNodesChange
TypeScript
onNodesChange(changes)
注意:
👉 changes 是一个数组!
🧩 Step 4:进入 applyNodeChanges
TypeScript
setNodes(nodes => applyNodeChanges(changes, nodes))
🧩 Step 5:更新 state
👉 返回新的 nodes
🧩 Step 6:React 重新渲染
👉 UI 更新完成
🔥 核心一:applyNodeChanges 到底干了什么?
旧 nodes
↓
遍历 changes
↓
根据 type 修改 node
↓
返回新 nodes
TypeScript
function applyNodeChanges(changes, nodes) {
let nextNodes = [...nodes];
changes.forEach(change => {
switch (change.type) {
case 'position':
nextNodes = nextNodes.map(node =>
node.id === change.id
? { ...node, position: change.position }
: node
);
break;
case 'remove':
nextNodes = nextNodes.filter(node => node.id !== change.id);
break;
case 'select':
nextNodes = nextNodes.map(node =>
node.id === change.id
? { ...node, selected: change.selected }
: node
);
break;
// 还有其他类型...
}
});
return nextNodes;
}
- position(拖动节点)2. remove(删除节点)3. select(选中节点)
🔥 核心二:applyEdgeChanges
🧠 一句话
👉 和 node 一模一样,只不过对象换成 edge
支持的操作:
- remove(删除线)
- select(选中线)
TypeScript
function applyEdgeChanges(changes, edges) {
let nextEdges = [...edges];
changes.forEach(change => {
switch (change.type) {
case 'remove':
nextEdges = nextEdges.filter(edge => edge.id !== change.id);
break;
case 'select':
nextEdges = nextEdges.map(edge =>
edge.id === change.id
? { ...edge, selected: change.selected }
: edge
);
break;
}
});
return nextEdges;
}
🔥 核心三:addEdge 到底做了什么?
🧠 触发时机
👉 用户拖一条线连接两个节点
🧩 Step 1:用户开始拖拽(还没触发 onConnect)
👉 从某个节点的 handle(小圆点)开始拖
此时内部状态
javascript
{
source: 'n1',
// target 还没确定
}
👉 这时候还没有 Connection,也没有 Edge
🧩 Step 2:拖到目标节点上(关键时刻)
当你松开鼠标那一刻:
👉 React Flow 才会生成一个 Connection
javascript
{
source: 'n1',
target: 'n2'
}
⚠️ 注意:
👉这里还没有 id
👉 还没有进入你的 edges state
🔥 Step 3:触发 onConnect(这里的params类型为 Connection)
javascript
onConnect(params)
🧠 官方为什么写成 Edge | Connection?
因为:
👉 你可以自己传 Edge 进去
比如:
TypeScript
onConnect={(params) => {
const edge = {
...params,
id: 'custom-id',
animated: true,
};
setEdges((eds) => [...eds, edge]);
}}
👉 这时候你手动把 Connection → Edge
🔥 Step 4:addEdge 干了什么(本质转换)
TypeScript
addEdge(params, edges)
🧠 addEdge 本质
TypeScript
function addEdge(connection, edges) {
const newEdge = {
id: `${connection.source}-${connection.target}`, // 自动生成id
source: connection.source,
target: connection.target,
...connection, // 你的 type / animated 等
};
return [...edges, newEdge];
}
🎯 重点总结
👉 addEdge 就是:
👉 帮你生成一条"合法的边"并加进数组
🔗 一张图串起所有流程
用户操作(拖动 / 点击 / 连线)
↓
ReactFlow 捕获事件
↓
生成 changes / connection
↓
onNodesChange / onEdgesChange / onConnect
↓
applyNodeChanges / applyEdgeChanges / addEdge
↓
setState
↓
React 重新渲染