React Flow 边事件处理实战:鼠标事件、键盘操作及连接规则设置(附完整代码)

本文为《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。

        边可以响应多种用户操作,如点击、双击、鼠标悬停等。通过绑定相应的事件处理函数,实现边的交互功能。点击边弹出其详细信息窗口;鼠标悬停时显示工具提示,说明边所代表的关系含义。

1 鼠标悬停

        下面程序为鼠标悬停事件示例,显示边的信息、改变线条颜色、宽度和线型。

import React, { useCallback, useState } 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 = [{ id: 'e1-2', source: '1', target: '2',type: 'default' }
];export default function FlowComponent() {const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);const [hoveredEdgeId, setHoveredEdgeId] = useState(null);const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)),[setEdges]);// 动态获取边样式const getEdgeStyle = (edge) => {return hoveredEdgeId === edge.id ? {stroke: 'red',strokeWidth: 3,strokeDasharray: '5 5',}: {};};return (<div style={{ height: '500px', width: '100%' }}><ReactFlownodes={nodes}edges={edges.map(edge => ({...edge,style: getEdgeStyle(edge)}))}onNodesChange={onNodesChange}onEdgesChange={onEdgesChange}onConnect={onConnect}onEdgeMouseEnter={(event, edge) => setHoveredEdgeId(edge.id)}onEdgeMouseLeave={() => setHoveredEdgeId(null)}fitView/></div>);
}

        运行程序后结果如下图所示。

图1 边 - 鼠标悬停

2 鼠标单击

        下面程序为鼠标单击事件示例,显示边的信息。

export default function App() {const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)),[setEdges],);const onEdgeClick = useCallback((event, edge) => {console.log('Clicked edge:', edge);// 这里可以添加更多逻辑,比如显示模态框等}, []);return (<div style={{ height: '500px' }}><ReactFlownodes={nodes}edges={edges}onNodesChange={onNodesChange}onEdgesChange={onEdgesChange}onConnect={onConnect}onEdgeClick={onEdgeClick}fitView/></div>);
}

        运行程序后结果如下图所示。

图2 边 - 鼠标单击

3 鼠标双击

        下面程序为鼠标双击事件示例。

export default function App() {const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)),[setEdges],);const handleEdgeDoubleClick = (event, edge) => {alert(`双击了边:${edge.id}`);// 这里可以添加更多自定义逻辑,比如:// - 删除边// - 编辑边属性// - 高亮关联节点等};return (<div style={{ height: '500px' }}><ReactFlownodes={nodes}edges={edges}onNodesChange={onNodesChange}onEdgesChange={onEdgesChange}onConnect={onConnect}onEdgeDoubleClick={handleEdgeDoubleClick}fitView/></div>);
}

4 键盘事件

        同样地,边也支持键盘事件,示例如下:

export default function App() {const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);// 处理连接线const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)),[setEdges],);// 键盘事件处理const handleKeyDown = useCallback((event) => {if (event.key === 'Delete') {alert('键盘Delete键被按下')}}, [setNodes, setEdges]);return (<div style={{ height: '500px', outline: 'none' }} tabIndex={0} onKeyDown={handleKeyDown}><ReactFlownodes={nodes}edges={edges}onNodesChange={onNodesChange}onEdgesChange={onEdgesChange}onConnect={onConnect}fitView/></div>);
}

5 连接事件

        在 React Flow 中,边的连接和断开是常见操作。当用户尝试连接两个节点时,系统需要验证连接的合法性,如检查节点的输入输出端口是否匹配、是否存在循环连接等。断开连接时需要处理相关的数据更新和视觉效果变化。可以通过onConnect 和 onEdgesDelete 事件进行自定义逻辑处理。

        onConnect示例程序如下所示。

export default function App() {const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);const onConnect = useCallback((params) => {// 查找源节点和目标节点const sourceNode = nodes.find(node => node.id === params.source);const targetNode = nodes.find(node => node.id === params.target);// 打印节点信息console.log('连接起始端点:', sourceNode);console.log('连接终止端点:', targetNode);// 添加连接边setEdges((eds) => addEdge(params, eds));},[setEdges, nodes] // 添加nodes依赖确保获取最新数据);return (<div style={{ height: '500px' }}><ReactFlownodes={nodes}edges={edges}onNodesChange={onNodesChange}onEdgesChange={onEdgesChange}onConnect={onConnect}fitView/></div>);
}

        运行程序后结果如下图所示。

图3 onConnect 连接

6 连接规则

        我们也可以设置连接规则,例如下面程序不允许自身内部进行连接。

import React, { useCallback } from 'react';
import { ReactFlow, Handle, useNodesState, useEdgesState, addEdge } from 'reactflow';
import 'reactflow/dist/style.css';
import { FiDatabase, FiCloud } from 'react-icons/fi';
import { toast, Toaster } from 'react-hot-toast'; // 添加Toast组件// 自定义节点组件 npm install react-hot-toast
const CustomNode = ({ id, data, selected }) => {return (<div className={`custom-node ${selected ? 'selected' : ''}`}><Handletype="target"position="top"className="!bg-teal-500"// isValidConnection={(connection) => //   connection.source !== id  // 禁止自连接// }/><div className="node-header"><FiCloud className="node-icon" /><h3 className="node-title">{data.label}</h3></div><div className="node-body"><FiDatabase className="node-icon" /><span className="node-info">{data.content}</span></div><Handletype="source"position="bottom"className="!bg-purple-500"/><Handletype="source"position="right"id={`${id}-output-2`}className="!bg-pink-500"style={{ top: '30%' }}/></div>);
};const initialNodes = [{ id: '1', position: { x: 0, y: 0 }, data: { label: '开始节点',content: '输入数据源'},type: 'custom',},{ id: '2', position: { x: 200, y: 150 }, data: { label: '处理节点',content: '数据处理流程'},type: 'custom',},
];const initialEdges = [{ id: 'e1-2', source: '1', target: '2',animated: true,style: { stroke: '#94a3b8' },
}];const nodeTypes = {custom: CustomNode,
};// 节点样式
const nodeStyle = `.custom-node {background: linear-gradient(145deg, #ffffff, #f1f5f9);border-radius: 8px;border: 2px solid #cbd5e1;box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);padding: 16px;min-width: 200px;transition: all 0.2s ease;}.custom-node.selected {border-color: #6366f1;box-shadow: 0 4px 15px rgba(99, 102, 241, 0.2);}.custom-node:hover {transform: translateY(-2px);}.node-header {display: flex;align-items: center;margin-bottom: 12px;border-bottom: 1px solid #e2e8f0;padding-bottom: 8px;}.node-title {margin: 0;font-size: 1.1rem;color: #1e293b;margin-left: 8px;}.node-body {display: flex;align-items: center;color: #64748b;}.node-icon {font-size: 1.2rem;margin-right: 8px;color: #6366f1;}.node-info {font-size: 0.9rem;}.react-flow__handle {width: 14px;height: 14px;border-radius: 3px;border: none;}
`;export default function App() {// 使用useNodesState管理节点状态const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);// 连接处理回调const onConnect = useCallback((connection) => {// 根据不同的输出端口设置边样式const edgeStyle = connection.sourceHandle?.endsWith('-output-2') ? { stroke: '#ec4899' } : { stroke: '#94a3b8' };return setEdges((eds) =>addEdge({...connection,animated: true,style: edgeStyle,}, eds));},[setEdges]);// 连接验证逻辑const isValidConnection = useCallback((connection) => {// 禁止自连接if (connection.source === connection.target) {toast.error('不能连接到自身');// alert('不能连接到自身');return false;}// 检查目标节点是否已有连接const targetConnections = edges.filter((edge) => edge.target === connection.target);if (targetConnections.length > 0) {toast.error('目标节点已有连接');console.log(`连接被禁止:节点 ${connection.target} 已有输入连接`);return false;}return true;},[edges]);return (<div style={{ height: '100vh', background: '#f8fafc' }}><style>{nodeStyle}</style><Toaster position="top-right" /> {/* Toast消息容器 */}<ReactFlow nodes={nodes}edges={edges}onNodesChange={onNodesChange}  // 添加状态变更处理器onEdgesChange={onEdgesChange}onConnect={onConnect}nodeTypes={nodeTypes}isValidConnection={isValidConnection}fitViewstyle={{ background: '#f8fafc' }}connectionLineStyle={{ stroke: '#94a3b8', strokeWidth: 2 }}defaultEdgeOptions={{type: 'smoothstep',animated: true,style: { strokeWidth: 2 }}}/></div>);
}

7 断开事件

        onEdgesDelete 示例程序如下所示。

export default function App() {const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);// 连接处理const onConnect = useCallback((params) => {const sourceNode = nodes.find(n => n.id === params.source);const targetNode = nodes.find(n => n.id === params.target);console.log('[连接建立] 起始节点:', sourceNode);console.log('[连接建立] 终止节点:', targetNode);setEdges((eds) => addEdge(params, eds));},[setEdges, nodes]);// 断开连接处理const onEdgesDeleted = useCallback((deletedEdges) => {deletedEdges.forEach(edge => {const sourceNode = nodes.find(n => n.id === edge.source);const targetNode = nodes.find(n => n.id === edge.target);console.log('[连接断开] 起始节点:', sourceNode);console.log('[连接断开] 终止节点:', targetNode);});},[nodes]);return (<div style={{ height: '500px' }}><ReactFlownodes={nodes}edges={edges}onNodesChange={onNodesChange}onEdgesChange={onEdgesChange}onConnect={onConnect}onEdgesDelete={onEdgesDeleted}  // 添加断开连接处理器fitView/></div>);
}

        运行程序后结果如下图所示。

图4 onEdgesDelete 连接断开

立即关注获取最新动态

点击订阅《React Agent 开发专栏》,每周获取智能体开发深度教程。项目代码持续更新至React Agent 开源仓库,欢迎 Star 获取实时更新通知!FGAI 人工智能平台:FGAI 人工智能平台

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84073.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java小结(一)

java&#xff08;上&#xff09; 模块一 1.JDK,JRE,JVM 知识点 核心内容 易混淆点 JDK定义 Java Development Kit&#xff08;Java开发工具包&#xff09;&#xff0c;包含开发所需全部工具 JDK包含JRE的关系容易混淆 JRE定义 Java Runtime Environment&#xff08;Jav…

ddns-go安装介绍-强大的ipv6动态域名解析神器-家庭云计算专家

ddns-go 是一款轻量级开源动态域名解析工具&#xff0c;专注于解决动态IP环境下的域名绑定问题&#xff0c;尤其适配IPv6网络环境。其核心功能包括&#xff1a; 1.IPv6动态解析&#xff1a;自动检测本地IPv6地址变化&#xff08;支持网卡、接口或命令获取&#xff09;&#xf…

Docker-mongodb

拉取 MongoDB 镜像: docker pull mongo 创建容器并设置用户&#xff1a; 要挂载本地数据目录&#xff0c;请替换此路径: /Users/Allen/Env/AllenDocker/mongodb/data/db docker run -d --name local-mongodb \-e MONGO_INITDB_ROOT_USERNAMEadmin \-e MONGO_INITDB_ROOT_PA…

WooCommerce缓存教程 – 如何防止缓存破坏你的WooCommerce网站?

我们在以前的文章中探讨过如何加快你的WordPress网站的速度&#xff0c;并研究过各种形式的缓存。 然而&#xff0c;像那些使用WooCommerce的动态电子商务网站&#xff0c;在让缓存正常工作方面往往会面临重大挑战。 在本指南中&#xff0c;我们将告诉你如何为WooCommerce设置…

贪心算法 Part04

总结下重叠区间问题 LC 452. 用最少数量的箭引爆气球 和 LC 435. 无重叠区间 本质上是一样的。 LC 452. 用最少数量的箭引爆气球 是求n个区间当中 &#xff0c; 区间的种类数量 k。此处可以理解为&#xff0c;重叠在一起的区间属于同一品种&#xff0c;没有重叠的区间当然…

云原生CD工具-Argocd+ArgoRollout入门到精通

第一章 Argo CD简介 课时1.1 Argo产品介绍 ARGO官网地址:https://argoproj.github.io/ 旗下产品有: Argo Workflows、ArgoCD 、Argo Rollouts 、Argo Events 课时1.2 什么是Argo CD Argo CD 是一个开源的持续交付工具, 是 Kubernetes 的声明式 GitOps 持续交付工具。专…

数据分析与应用---数据可视化基础

目录 Matplotlib基础绘图 (一)、pyplot绘图基础语法与常用参数 1、pyplot基础语法 (1) 创建画布与创建子图 (2) 添加画布内容 (3) 保存与显示图形 案例代码 2. 设置pyplot的动态rc参数 (二)、使用Matplotlib绘制进阶图形 1. 绘制散点图----scatter 2. 绘制折线…

PP-YOLOE-SOD学习笔记1

项目&#xff1a;基于PP-YOLOE-SOD的无人机航拍图像检测案例全流程实操 - 飞桨AI Studio星河社区 一、安装环境 先准备新环境py>3.9 1.先cd到源代码的根目录下 2.pip install -r requirements.txt 3.python setup.py install 这一步需要看自己的GPU情况&#xff0c;去飞浆…

力扣HOT100之二叉树:114. 二叉树展开为链表

这道题自己尝试着做了一下&#xff0c;感觉还是得用递归来做比较简单&#xff0c;但是一直想的是用前序遍历来构造链表&#xff0c;导致怎么做都不对&#xff0c;去看了下灵神的题解&#xff0c;然后问了下GPT&#xff0c;现在终于弄明白了。虽然构造出来的链表的排列顺序是按照…

Spring Boot 注解 @ConditionalOnMissingBean是什么

一句话总结&#xff1a; ConditionalOnMissingBean 是 Spring Boot 提供的一个 条件注解&#xff08;Conditional Annotation&#xff09;&#xff0c;意思是&#xff1a; 只有当 Spring 容器中 不存在 某个 Bean 时&#xff0c;当前的 Bean 或配置才会被加载。 这是一种典型的…

PyInstaller 如何在mac电脑上生成在window上可执行的exe文件

PyInstaller跨平台打包限制 PyInstaller 无法直接从macOS生成Windows可执行文件&#xff0c;因为它需要访问目标平台的系统库和Python环境来构建可执行文件。要在macOS上为Windows打包Python应用&#xff0c;需要通过以下方法之一&#xff1a; 方法一&#xff1a;使用虚拟机或…

零基础设计模式——创建型模式 - 抽象工厂模式

第二部分&#xff1a;创建型模式 - 抽象工厂模式 (Abstract Factory Pattern) 我们已经学习了单例模式&#xff08;保证唯一实例&#xff09;和工厂方法模式&#xff08;延迟创建到子类&#xff09;。现在&#xff0c;我们来探讨创建型模式中更为复杂和强大的一个——抽象工厂…

【通用智能体】Serper API 详解:搜索引擎数据获取的核心工具

Serper API 详解&#xff1a;搜索引擎数据获取的核心工具 一、Serper API 的定义与核心功能二、技术架构与核心优势2.1 技术实现原理2.2 对比传统方案的突破性优势 三、典型应用场景与代码示例3.1 SEO 监控系统3.2 竞品广告分析 四、使用成本与配额策略五、开发者注意事项六、替…

Flask-SQLAlchemy核心概念:模型类与数据库表、类属性与表字段、外键与关系映射

前置阅读&#xff0c;关于Flask-SQLAlchemy支持哪些数据库及基本配置&#xff0c;链接&#xff1a;Flask-SQLAlchemy_数据库配置 摘要 本文以一段典型的 SQLAlchemy 代码示例为引入&#xff0c;阐述以下核心概念&#xff1a; 模型类&#xff08;Model Class&#xff09; ↔ 数…

野火鲁班猫(arrch64架构debian)从零实现用MobileFaceNet算法进行实时人脸识别(四)安装RKNN Toolkit2

RKNN Toolkit2是用来将onnx模型转成rknn专用模型&#xff0c;并可通过RKNN Toolkit Lite2或者RKNPU调用NPU进行加速计算的工具。 一开始我安装很多次都无法成功安装。后来跟售后技术对接&#xff0c;必须是PC平台的Linux环境才可以。我的电脑是windows&#xff0c;所以我需要用…

基于深度学习的工件检测系统设计与实现

在工业自动化领域&#xff0c;工件检测一直是提高生产效率和产品质量的关键环节。传统的人工检测方法不仅效率低下&#xff0c;而且容易受到主观因素的影响&#xff0c;导致误判率较高。随着深度学习技术的飞速发展&#xff0c;基于图像识别的自动检测系统逐渐成为研究热点。今…

CyberSecAsia专访CertiK首席安全官:区块链行业亟需“安全优先”开发范式

近日&#xff0c;权威网络安全媒体CyberSecAsia发布了对CertiK首席安全官Wang Tielei博士的专访&#xff0c;双方围绕企业在进军区块链领域时所面临的关键安全风险与防御策略展开深入探讨。 Wang博士在采访中指出&#xff0c;跨链桥攻击、智能合约漏洞以及私钥管理不当&#x…

Google C++ Style Guide 谷歌 C++编码风格指南,深入理解华为与谷歌的编程规范——C和C++实践指南

Google C 编程风格指南 Release Apr 07, 2017 0. ᡿享 ⡾ᵢ 4.45 ৕֒㘻 Benjy Weinberger, Craig Silverstein, Gregory Eitzmann, Mark Mentovai, Tashana Landray 㘱䈇 YuleFox, Yang.Y, acgtyrant, lilinsanity 亯ⴤѱ享 • Google Style Guide • Google 开源…

当科技邂逅浪漫:在Codigger的世界里,遇见“爱”

520&#xff0c;一个充满爱意的日子&#xff0c;人们用各种方式表达对彼此的深情。而在科技的世界里&#xff0c;我们也正经历着一场特别的邂逅——Codigger&#xff0c;一个分布式操作系统的诞生&#xff0c;正在以它独特的方式&#xff0c;重新定义我们与技术的关系。 Codigg…

嵌入式学习笔记 - Void类型的指针

void指针的基本概念和特性 void指针是一种特殊的指针类型&#xff0c;称为“无类型指针”或“通用指针”。它的主要特点是&#xff1a; ‌通用性‌&#xff1a;void指针可以指向任何类型的数据&#xff0c;这使得它在处理不确定数据类型时非常有用。 ‌灵活性‌&#xff1a;由…