自定义边
就像 自定义节点,React Flow 中自定义边的部分也仅仅是 React 组件:这意味着你可以在边上渲染任何你想要的东西!本指南向你展示了如何实现一个带有额外控件的自定义边。
一个基本的自定义边
如果边不能在两个连接的节点之间渲染路径,它对我们就没什么用。这些路径始终基于 SVG,通常使用 <BaseEdge /> 组件渲染。为了计算要渲染的实际 SVG 路径,React Flow 提供了一些方便的实用函数
为了开始我们的自定义边,我们只渲染源节点和目标节点之间的直线路径。
import { BaseEdge, getStraightPath } from '@xyflow/react';
 
export default function CustomEdge({ id, sourceX, sourceY, targetX, targetY }) {
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
  });
 
  return (
    <>
      <BaseEdge id={id} path={edgePath} />
    </>
  );
}传递给自定义边组件的所有属性可以在 API 参考中找到 EdgeProps 类型下。
这给了我们一个与默认的 "straight" 边类型 行为相同的直线边。为了使用它,我们还需要更新 <ReactFlow /> 组件上的 edgeTypes 属性。
在组件外部定义 edgeTypes 对象或使用 React 的 useMemo 钩子以防止不必要的重新渲染非常重要。如果你忘记这样做,React Flow 会在控制台中显示警告。
import ReactFlow from '@xyflow/react'
import CustomEdge from './CustomEdge'
 
 
const edgeTypes = {
  'custom-edge': CustomEdge
}
 
export function Flow() {
  return <ReactFlow edgeTypes={edgeTypes} ... />
}在定义 edgeTypes 对象后,我们可以通过将边的 type 字段设置为 "custom-edge" 来使用新的自定义边。
添加边标签
自定义边的最常见用途之一是在边的路径上渲染一些控件或信息。在 React Flow 中,我们称之为边标签,与边路径不同,边标签可以是任何 React 组件!
为了渲染自定义边标签,我们必须将其包装在 <EdgeLabelRenderer /> 组件中。这样做是为了性能原因:边标签渲染器是一个门户,指向一个所有边标签都渲染到其中的单个容器。
让我们在自定义边上添加一个按钮,可以用来删除它所连接的边
import {
  BaseEdge,
  EdgeLabelRenderer,
  getStraightPath,
  useReactFlow,
} from '@xyflow/react';
 
export default function CustomEdge({ id, sourceX, sourceY, targetX, targetY }) {
  const { setEdges } = useReactFlow();
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
  });
 
  return (
    <>
      <BaseEdge id={id} path={edgePath} />
      <EdgeLabelRenderer>
        <button
          onClick={() => setEdges((edges) => edges.filter((e) => e.id !== id))}
        >
          delete
        </button>
      </EdgeLabelRenderer>
    </>
  );
}如果我们现在尝试使用这条边,我们会看到按钮被渲染在流程的中心(它可能被“节点 A”隐藏)。由于边标签门户的存在,我们需要做一些额外的工作来自己定位按钮。

幸运的是,我们已经见过的路径工具可以帮助我们!除了要渲染的 SVG 路径之外,这些函数还返回路径中点的 x 和 y 坐标。然后,我们可以使用这些坐标将自定义边标签转换为正确的定位!
export default function CustomEdge({ id, sourceX, sourceY, targetX, targetY }) {
  const { setEdges } = useReactFlow();
  const [edgePath, labelX, labelY] = getStraightPath({ ... });
 
  return (
    ...
        <button
          style={{
            position: 'absolute',
            transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
            pointerEvents: 'all',
          }}
          className="nodrag nopan"
          onClick={() => {
            setEdges((es) => es.filter((e) => e.id !== id));
          }}
        >
    ...
  );
}为了确保我们的边标签具有交互性,而不仅仅是为了展示,给标签的样式添加 pointer-events: all 非常重要。这样可以确保标签是可点击的。
就像自定义节点中的交互控件一样,我们需要记住将 nodrag 和 nopan 类添加到标签中,以阻止鼠标事件控制画布。
这是一个使用更新后的自定义边的交互示例。点击删除按钮会从流程中移除该边。创建新边会使用自定义节点。