与 TypeScript 一起使用
🌐 Usage with TypeScript
React Flow 是用 TypeScript 编写的,因为我们重视它提供的额外安全保障。我们导出你需要的所有类型,以便正确地为传递给 React Flow 组件的数据结构和函数进行类型标注。我们还提供了一种扩展节点和边类型的方法。
🌐 React Flow is written in TypeScript because we value the additional safety barrier it provides. We export all the types you need for correctly typing data structures and functions you pass to the React Flow component. We also provide a way to extend the types of nodes and edges.
基本用法
🌐 Basic usage
让我们从最基本的类型开始,这是一个简单的起点。TypeScript 可能已经能够推断出其中的一些类型,但我们仍然会明确地定义它们。
🌐 Let’s start with the most basic types you need for a simple starting point. Typescript might already infer some of these types, but we will define them explicitly nonetheless.
import { useState, useCallback } from 'react';
import {
ReactFlow,
addEdge,
applyNodeChanges,
applyEdgeChanges,
type Node,
type Edge,
type FitViewOptions,
type OnConnect,
type OnNodesChange,
type OnEdgesChange,
type OnNodeDrag,
type DefaultEdgeOptions,
} from '@xyflow/react';
const initialNodes: Node[] = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 5, y: 5 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 5, y: 100 } },
];
const initialEdges: Edge[] = [{ id: 'e1-2', source: '1', target: '2' }];
const fitViewOptions: FitViewOptions = {
padding: 0.2,
};
const defaultEdgeOptions: DefaultEdgeOptions = {
animated: true,
};
const onNodeDrag: OnNodeDrag = (_, node) => {
console.log('drag event', node.data);
};
function Flow() {
const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState<Edge[]>(initialEdges);
const onNodesChange: OnNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes],
);
const onEdgesChange: OnEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges],
);
const onConnect: OnConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges],
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onNodeDrag={onNodeDrag}
fitView
fitViewOptions={fitViewOptions}
defaultEdgeOptions={defaultEdgeOptions}
/>
);
}自定义节点
🌐 Custom nodes
在使用自定义节点时,你可以将自定义的 Node 类型(或你的 Node 联合类型)传递给 NodeProps 类型。基本上有两种方法可以使用自定义节点:
🌐 When working with custom nodes you have the possibility to pass a custom Node type (or your Node union) to the NodeProps type. There are basically two ways to work with custom nodes:
- 如果你有多个自定义节点,你会想将特定的
Node类型作为通用类型传递给NodeProps类型:
import type { Node, NodeProps } from '@xyflow/react';
type NumberNode = Node<{ number: number }, 'number'>;
export default function NumberNode({ data }: NodeProps<NumberNode>) {
return <div>A special number: {data.number}</div>;
}⚠️ 如果你单独指定节点数据,你需要使用 type(这里使用 interface 是行不通的):
type NumberNodeData = { number: number };
type NumberNode = Node<NumberNodeData, 'number'>;- 如果你有一个自定义节点,它根据节点类型呈现不同的内容,你想将你的
Node联合类型作为泛型传递给NodeProps:
import type { Node, NodeProps } from '@xyflow/react';
type NumberNode = Node<{ number: number }, 'number'>;
type TextNode = Node<{ text: string }, 'text'>;
type AppNode = NumberNode | TextNode;
export default function CustomNode({ data }: NodeProps<AppNode>) {
if (data.type === 'number') {
return <div>A special number: {data.number}</div>;
}
return <div>A special text: {data.text}</div>;
}自定义边
🌐 Custom edges
对于自定义边,你和自定义节点一样有相同的可能性。
🌐 For custom edges you have the same possibility as for custom nodes.
import { getStraightPath, BaseEdge, type EdgeProps, type Edge } from '@xyflow/react';
type CustomEdge = Edge<{ value: number }, 'custom'>;
export default function CustomEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
}: EdgeProps<CustomEdge>) {
const [edgePath] = getStraightPath({ sourceX, sourceY, targetX, targetY });
return <BaseEdge id={id} path={edgePath} />;
}高级用法
🌐 Advanced usage
在使用 React Flow 创建复杂应用时,你会有许多自定义节点和边,每个节点和边都附带不同类型的数据。当我们通过内置函数和钩子操作这些节点和边时,我们必须确保将节点和边的类型缩小 以防止运行时错误。
🌐 When creating complex applications with React Flow, you will have a number of custom nodes & edges, each with different kinds of data attached to them. When we operate on these nodes & edges through built in functions and hooks, we have to make sure that we narrow down the types of nodes & edges to prevent runtime errors.
Node 和 Edge 类型的联合
🌐 Node and Edge type unions
你会看到许多函数、回调和钩子(甚至是 ReactFlow 组件本身)都期望一个 NodeType 或 EdgeType 泛型。这些泛型是你应用中所有不同类型节点和边的联合 。只要你正确地为数据对象进行了类型定义(见前一节),你就可以使用它们导出的类型。
🌐 You will see many functions, callbacks and hooks (even the ReactFlow component itself) that expect a NodeType or EdgeType generic. These generics are
unions of all the different types of nodes & edges you have in your application.
As long as you have typed the data objects correctly (see previous section), you can use their exported type.
如果你使用任何内置节点(‘input’、‘output’、‘default’)或边(‘straight’、‘step’、‘smoothstep’、‘bezier’),你可以将从 @xyflow/react 导出的 BuiltInNode 和 BuiltInEdge 类型添加到你的联合类型中。
import type { BuiltInNode, BuiltInEdge } from '@xyflow/react';
// Custom nodes
import NumberNode from './NumberNode';
import TextNode from './TextNode';
// Custom edge
import EditableEdge from './EditableEdge';
export type CustomNodeType = BuiltInNode | NumberNode | TextNode;
export type CustomEdgeType = BuiltInEdge | EditableEdge;传递给 <ReactFlow /> 的函数
🌐 Functions passed to <ReactFlow />
为了为回调函数接收正确的类型,你可以将联合类型传递给 ReactFlow 组件。通过这样做,你将必须显式地为回调函数指定类型。
🌐 To receive correct types for callback functions, you can pass your union types to the ReactFlow component.
By doing that you will have to type your callback functions explicitly.
import { type OnNodeDrag } from '@xyflow/react';
// ...
// Pass your union type here ...
const onNodeDrag: OnNodeDrag<CustomNodeType> = useCallback((_, node) => {
if (node.type === 'number') {
// From here on, Typescript knows that node.data
// is of type { num: number }
console.log('drag event', node.data.number);
}
}, []);
const onNodesChange: OnNodesChange<CustomNodeType> = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes],
);钩子
🌐 Hooks
类型联合还可用于键入许多钩子的返回值。
🌐 The type unions can also be used to type the return values of many hooks.
import { useReactFlow, useNodeConnections, useNodesData, useStore } from '@xyflow/react';
export default function FlowComponent() {
// returned nodes and edges are correctly typed now
const { getNodes, getEdges } = useReactFlow<CustomNodeType, CustomEdgeType>();
// You can type useStore by typing the selector function
const nodes = useStore((s: ReactFlowState<CustomNodeType>) => s.nodes);
const connections = useNodeConnections({
handleType: 'target',
});
const nodesData = useNodesData<CustomNodeType>(connections?.[0].source);
nodeData.forEach(({ type, data }) => {
if (type === 'number') {
// This is type safe because we have narrowed down the type
console.log(data.number);
}
});
// ...
}类型保护
🌐 Type guards
在 TypeScript 中,你可以通过多种方式定义type guards 。
一种方法是定义像 isNumberNode 或 isTextNode 这样的类型保护函数,从节点列表中过滤出特定的节点。
🌐 There are multiple ways you can define type guards in Typescript.
One way is to define type guard functions like isNumberNode or isTextNode to filter out specific nodes from a list of nodes.
function isNumberNode(node: CustomNodeType): node is NumberNode {
return node.type === 'number';
}
// numberNodes is of type NumberNode[]
const numberNodes = nodes.filter(isNumberNode);