本文为《React Agent:从零开始构建 AI 智能体》专栏系列文章。 专栏地址:https://blog.csdn.net/suiyingy/category_12933485.html。项目地址:https://gitee.com/fgai/react-agent(含完整代码示例与实战源)。完整介绍:https://blog.csdn.net/suiyingy/article/details/146983582。
在 React Flow 里,数据管控涵盖数据管理、存储与加载等多方面,对构建高效、灵活且可交互的流程图应用极为重要。
数据管理是 React Flow 的核心功能之一,它主要负责节点和边状态的维护。借助 useNodesState 和 useEdgesState 钩子函数,开发者能够对节点和边的状态进行实时更新与渲染。例如,当用户添加、删除或移动节点与边时,通过修改相应的状态,React Flow 会自动重新渲染流程图,确保界面与用户操作同步。同时,数据管理还保证了节点和边之间数据的一致性与同步性,在修改某个元素时,与之关联的其他元素也会同步更新,维持流程图的逻辑完整性。
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
useNodesState 用于管理图中节点(nodes)的状态。它会返回一个数组,其中包含当前节点状态、更新节点状态的函数以及处理节点变化的回调函数。useEdgesState 则是专门用于管理图中边(edges)的状态。同样地,它也返回一个数组,包含当前边的状态、更新边状态的函数以及处理边变化的回调函数。
代码中的 const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); 语句,调用 useNodesState 钩子并传入初始节点数据 initialNodes。这里返回的 nodes 是当前的节点状态,setNodes 是用于手动更新节点状态的函数,onNodesChange 是处理节点变化的回调函数,通常在节点发生移动、调整大小等操作时被触发。
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); 语句同理,调用 useEdgesState 钩子并传入初始边数据 initialEdges。edges 表示当前边的状态,setEdges 用于手动更新边的状态,onEdgesChange 是处理边变化的回调函数,在边的连接关系发生改变等情况下会被调用。
useState 是 React 中的一个内置钩子,其用途是在函数式组件里添加状态管理功能。相比于 useNodesState 和 useEdgesState,它可以自定义除节点和边之外的功能。它 接收一个初始状态值作为参数,并且返回一个数组,该数组包含两个元素:当前状态值和用于更新状态的函数。每当调用更新状态的函数时,React 会重新渲染组件,同时使用新的状态值。这使得函数式组件能够像类组件一样对状态进行管理和响应式更新,增强了函数式组件的灵活性和复用性。
// 将此文件内容复制到App.js,运行npm start即可查看效果。
import React, { useState } from 'react';
const Counter = () => {
// 使用 useState 初始化计数器状态,初始值为 0
const [count, setCount] = useState(0);
// 定义一个函数来增加计数器的值
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>计数器的值是: {count}</p>
<button onClick={increment}>增加</button>
</div>
);
};
export default Counter;
数据存储功能允许开发者将当前流程图的状态保存下来,以备后续使用。这在需要保存用户的工作进度、实现流程图的版本管理等场景中非常有用。开发者可以将节点和边的数据存储到本地存储(如浏览器的 localStorage)或服务器端数据库中。例如,当用户完成一个复杂的流程图设计后,将其保存到本地,下次打开时可以继续编辑。存储的数据格式通常是 JSON,它易于解析和存储,并且能够完整地保留节点和边的各种属性。
与数据存储相对应,数据加载功能能够从存储介质(如本地存储或服务器)中读取之前保存的流程图数据,并将其恢复到 React Flow 中。在应用启动时或者用户需要查看之前保存的流程图时,通过加载存储的数据,能够快速还原流程图的状态。这不仅提高了用户的工作效率,还为多用户协作、数据共享等场景提供了支持。例如,在多人协作的流程图设计项目中,不同用户可以加载相同的流程图数据进行编辑和修改。
在一些简单的场景中,可以将节点或边等数据存储在浏览器的本地存储(localStorage)中。这便于用户在不同会话之间保留图表状态。可以将节点和边的数据序列化为 JSON 格式,然后存储到 localStorage 中。加载图表时,再从 localStorage 中读取数据并反序列化。下面示例程序可将节点和边信息保存到本地并进行加载。
import React, { useCallback, useEffect } from 'react';
import {
ReactFlow,
useNodesState,
useEdgesState,
addEdge,
} from 'reactflow';
import 'reactflow/dist/style.css';
const initialNodes = [
{ id: '1', position: { x: 0, y: 0 }, data: { label: '1' } },
{ id: '2', position: { x: 0, y: 100 }, data: { label: '2' } },
];
const initialEdges = []; // 初始边设为空数组
const storageKey = 'react-flow-data';
export default function App() {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
// 初始化加载数据
useEffect(() => {
const loadData = () => {
const savedData = localStorage.getItem(storageKey);
if (savedData) {
try {
const { nodes: savedNodes, edges: savedEdges } = JSON.parse(savedData);
setNodes(savedNodes || []);
setEdges(savedEdges || []);
} catch (e) {
setNodes(initialNodes);
setEdges(initialEdges);
}
} else {
setNodes(initialNodes);
setEdges(initialEdges);
}
};
loadData();
}, [setNodes, setEdges]);
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
const handleSave = () => {
const flowData = { nodes, edges };
localStorage.setItem(storageKey, JSON.stringify(flowData));
alert('保存成功');
};
const handleLoad = () => {
const savedData = localStorage.getItem(storageKey);
if (!savedData) {
alert('没有找到保存数据');
return;
}
try {
const { nodes: loadedNodes, edges: loadedEdges } = JSON.parse(savedData);
setNodes(loadedNodes || []);
setEdges(loadedEdges || []);
alert('加载成功');
} catch (e) {
alert('数据解析失败');
}
};
return (
<div style={{ height: '500px' }}>
<div style={{
padding: '10px',
backgroundColor: '#f5f5f5',
borderBottom: '1px solid #ddd'
}}>
<button
onClick={handleSave}
style={{
marginRight: 10,
padding: '8px 20px',
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
borderRadius: 4,
cursor: 'pointer'
}}
>
保存
</button>
<button
onClick={handleLoad}
style={{
padding: '8px 20px',
backgroundColor: '#2196F3',
color: 'white',
border: 'none',
borderRadius: 4,
cursor: 'pointer'
}}
>
加载
</button>
</div>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
/>
</div>
);
}
图1 本地保存与加载
更复杂的应用通常需要将节点数据存储在后端服务器上。这涉及到与后端 API 的交互,包括发送 POST 请求保存数据,以及发送 GET 请求加载数据。React 组件可以使用fetch或其他 HTTP 客户端库(如 Axios)来实现与后端的通信。下一节将提供详细的实现示例。
import axios from 'axios';
// 保存节点数据到后端
const saveNodesToBackend = async (nodes) => {
try {
const response = await axios.post('/api/nodes', nodes);
return response.data;
} catch (error) {
console.error('Error saving nodes:', error);
return null;
}
};
// 从后端加载节点数据
const loadNodesFromBackend = async () => {
try {
const response = await axios.get('/api/nodes');
return response.data;
} catch (error) {
console.error('Error loading nodes:', error);
return [];
}
};
在一些实时性要求较高的应用中,如协作绘图工具,节点数据需要实时更新。这可以通过 WebSocket 等技术实现。当一个用户对节点进行操作(如移动节点、修改节点数据)时,将更新信息通过 WebSocket 发送到服务器,服务器再将更新广播给其他在线用户,实现数据的实时同步。
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = () => {
console.log('WebSocket connection established');
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
// 根据接收到的数据更新本地节点状态
// 假设数据格式包含节点ID和更新后的属性
const { nodeId, updatedProps } = data;
setNodes(prevNodes => prevNodes.map(node => {
if (node.id === nodeId) {
return {...node,...updatedProps };
}
return node;
}));
};
const handleNodeUpdate = (nodeId, updatedProps) => {
// 向服务器发送节点更新消息
const message = JSON.stringify({ nodeId, updatedProps });
socket.send(message);
};
上述代码首先建立 WebSocket 连接,当接收到服务器发送的消息时,解析消息数据并更新本地的节点状态。程序同时定义了handleNodeUpdate函数,用于在节点发生变化时将更新信息发送到服务器,进而实现多用户之间的实时同步。
我们也可以同时更新多个节点数据的情况,这时采用批量更新的方式可以减少与服务器的交互次数,提高性能。可以将多个节点的更新操作封装成一个请求发送到服务器。例如:
const batchUpdateNodes = async (nodesToUpdate) => {
try {
const response = await axios.post('/api/batch - update - nodes', nodesToUpdate);
return response.data;
} catch (error) {
console.error('Error batch updating nodes:', error);
return null;
}
};
// 使用示例
const nodesToUpdate = [
{ id: 'node - 1', data: { label: 'Updated Node 1' } },
{ id: 'node - 2', data: { label: 'Updated Node 2' } }
];
batchUpdateNodes(nodesToUpdate).then(result => {
if (result) {
// 更新本地节点数据
setNodes(result);
}
});
随着节点数据的不断更新,数据版本控制可以帮助追踪数据的变化历史,方便回滚操作以及进行数据审计。程序可以在节点数据中添加版本号字段,每次数据更新时版本号递增。服务器端同时存储每个版本的节点数据。例如:
const initialNode = {
id: 'node - 1',
type: 'default',
data: { label: 'Initial Node' },
position: { x: 100, y: 100 },
version: 1
};
const updateNode = (node) => {
return {
...node,
data: { label: 'Updated Node' },
version: node.version + 1
};
};
立即关注获取最新动态
点击订阅《React Agent 开发专栏》,每周获取智能体开发深度教程。项目代码持续更新至React Agent 开源仓库,欢迎 Star 获取实时更新通知!FGAI 人工智能平台:FGAI 人工智能平台