自定义边

就像 自定义节点,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" 来使用新的自定义边。

export default function App() {
  const data: string = "world"

  return <h1>Hello {data}</h1>
}

只读

添加边标签

自定义边的最常见用途之一是在边的路径上渲染一些控件或信息。在 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”隐藏)。由于边标签门户的存在,我们需要做一些额外的工作来自己定位按钮。

A screen shot of a simple flow. The edge label renderer is highlighted in the DOM inspector and the button is rendered in the centre of the flow.

幸运的是,我们已经见过的路径工具可以帮助我们!除了要渲染的 SVG 路径之外,这些函数还返回路径中点的 xy 坐标。然后,我们可以使用这些坐标将自定义边标签转换为正确的定位!

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 非常重要。这样可以确保标签是可点击的。

就像自定义节点中的交互控件一样,我们需要记住将 nodragnopan 类添加到标签中,以阻止鼠标事件控制画布。

这是一个使用更新后的自定义边的交互示例。点击删除按钮会从流程中移除该边。创建新边会使用自定义节点。

export default function App() {
  const data: string = "world"

  return <h1>Hello {data}</h1>
}

只读