迁移到 v10
欢迎使用 React Flow v10!随着主要版本更新,将带来许多新功能,但也有一些重大变更。
新功能
- 子流程: 您现在可以将节点添加到父节点,并创建组和嵌套流程
- 节点类型 ‘group’: 一种没有句柄的新节点类型,可用作组节点
- 触摸设备支持: 现在可以在触摸设备上连接节点
- 初始化时调整视图大小: 您可以使用新的
fitView
属性来调整初始视图的大小 - 按键处理: 现在不仅支持单个按键,还支持多个按键和按键组合
- useKeyPress 钩子: 用于处理键盘事件的实用钩子
- useReactFlow 钩子: 返回一个 React Flow 实例,该实例公开用于操作流程的功能
- useNodes,useEdges 和 useViewport 钩子: 用于接收节点、边和视窗的钩子
- 边标记: 更多选项来配置边的开始和结束标记
重大变更
TLDR
- 将
elements
数组拆分为nodes
和edges
数组,并实现onNodesChange
和onEdgesChange
处理程序(核心概念部分 中有详细指南) - 记忆你的自定义
nodeTypes
和edgeTypes
- 将
onLoad
重命名为onInit
- 将
paneMoveable
重命名为panOnDrag
- 将
useZoomPanHelper
重命名为useReactFlow
(以及setTransform
重命名为setViewport
) - 将节点和边选项
isHidden
重命名为hidden
重大变更的详细说明
1. 元素 - 节点和边
我们看到很多人在使用半受控的 elements
属性时遇到了困难。始终难以将本地用户状态与 React Flow 的内部状态同步。有些人使用了从未记录过的内部存储,这始终是一种不太好的解决方案。在新版本中,我们提供了两种使用 React Flow 的方式 - 非受控和受控。
1.1. 受控 nodes
和 edges
如果您想完全控制并使用来自本地状态或存储的节点和边,您可以结合使用 nodes
、edges
属性和 onNodesChange
和 onEdgesChange
处理程序。对于交互式流程,您需要实现这些处理程序(如果您仅对平移和缩放感到满意,则不需要它们)。当节点(s)被初始化、拖动、选中或移除时,您将收到一个更改。这意味着您始终知道节点的精确位置和尺寸,或者例如它是否被选中。我们导出了辅助函数 applyNodeChanges
和 applyEdgeChanges
,您应该使用它们来应用更改。
旧 API
import { useState, useCallback } from 'react';
import { ReactFlow, removeElements, addEdge } from 'react-flow-renderer';
const initialElements = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
{ id: 'e1-2', source: '1', target: '2' },
];
const BasicFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = useCallback(
(elementsToRemove) =>
setElements((els) => removeElements(elementsToRemove, els)),
[],
);
const onConnect = useCallback((connection) =>
setElements((es) => addEdge(connection, es)),
);
return (
<ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
/>
);
};
export default BasicFlow;
新 API
import { useState, useCallback } from 'react';
import {
ReactFlow,
applyNodeChanges,
applyEdgeChanges,
addEdge,
} from 'react-flow-renderer';
const initialNodes = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
const BasicFlow = () => {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const onNodesChange = useCallback(
(changes) => setNodes((ns) => applyNodeChanges(changes, ns)),
[],
);
const onEdgesChange = useCallback(
(changes) => setEdges((es) => applyEdgeChanges(changes, es)),
[],
);
const onConnect = useCallback((connection) =>
setEdges((eds) => addEdge(connection, eds)),
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
/>
);
};
export default BasicFlow;
您还可以使用新的钩子 useNodesState
和 useEdgesState
来快速入门
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
相关变更
onElementsClick
->onNodeClick
和onEdgeClick
onElementsRemove
-> 被onNodesChange
和onEdgesChange
处理程序替换
1.2 非受控 defaultNodes
和 defaultEdges
最简单的入门方法是使用 defaultNodes
和 defaultEdges
属性。设置这些属性后,所有操作都在内部处理。您无需添加任何其他处理程序即可获得完全交互式的流程,并具有拖动节点、连接节点以及删除节点和边的功能。
新 API
import ReactFlow from 'react-flow-renderer';
const defaultNodes = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
];
const defaultEdges = [{ id: 'e1-2', source: '1', target: '2' }];
const BasicFlow = () => {
return <ReactFlow defaultNodes={defaultNodes} defaultEdges={defaultEdges} />;
};
export default BasicFlow;
如果您想添加、删除或更新节点或边,您只能使用 ReactFlow 实例,您可以通过新的 useReactFlow
钩子或使用将实例作为函数参数的 onInit
处理程序来接收该实例。
2. 记忆您的自定义 nodeTypes
和 edgeTypes
每次您传递新的节点或边类型时,我们都会在后台创建包装的节点或边组件类型。这意味着您不应该在每次渲染时创建一个新的 nodeType
或 edgeType
对象。记忆您的 nodeTypes 和 edgeTypes,或者在它们不变时在组件外部定义它们。
不要这样做
这会在每次渲染时创建一个新对象,并导致错误和性能问题
// this is bad! Don't do it.
<ReactFlow
nodes={[]}
nodeTypes={{
specialType: SpecialNode, // bad!
}}
/>
这样做
function Flow() {
const nodeTypes = useMemo(() => ({ specialType: SpecialNode }), []);
return <ReactFlow nodes={[]} nodeTypes={nodeTypes} />;
}
或者在类型不变时在组件外部创建它们
const nodeTypes = { specialType: SpecialNode };
function Flow() {
return <ReactFlow nodes={[]} nodeTypes={nodeTypes} />;
}
3. Redux - Zustand
我们将状态管理库从 Redux 切换到 Zustand。通过这种更改,我们能够从与状态相关的代码中删除大约 300LOC。如果您需要访问内部存储,您可以使用 useStore
钩子
旧 API
import { useStoreState, useStoreActions } from 'react-flow-renderer';
...
const transform = useStoreState((store) => store.transform);
新 API
import { useStore } from 'react-flow-renderer';
...
const transform = useStore((store) => store.transform);
如果您想访问内部存储,您仍然需要使用 <ReactFlowProvider />
包装您的组件。
我们还导出 useStoreApi
,如果您需要在事件处理程序中获取存储,例如,无需触发重新渲染。
import { useStoreApi } from 'react-flow-renderer';
...
const store = useStoreApi();
...
// in an event handler
const [x, y, zoom] = store.getState().transform;
4. onLoad - onInit
onLoad
回调已重命名为 onInit
,现在在节点初始化时触发。
旧 API
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.zoomTo(2);
...
<ReactFlow
...
onLoad={onLoad}
/>
新 API
const onInit = (reactFlowInstance: ReactFlowInstance) => reactFlowInstance.zoomTo(2);
...
<ReactFlow
...
onInit={onInit}
/>
5. paneMoveable - panOnDrag
这与 API 的其他部分 (panOnScroll
, zoomOnScroll
等) 更加一致。
旧 API
<ReactFlow
...
paneMoveable={false}
/>
新 API
<ReactFlow
...
panOnDrag={false}
/>
6. useZoomPanHelper transform - 在 useReactFlow
中统一
由于“transform”也是存储中变换的变量名称,而且不清楚 transform
是一个 setter,因此我们将它重命名为 setViewport
。这也与其他函数更加一致。此外,所有 useZoomPanHelper
函数已移至 React Flow 实例,您可以从 useReactFlow
钩子 或 onInit
处理程序获取该实例。
旧 API
const { transform, setCenter, setZoom } = useZoomPanHelper();
...
transform({ x: 100, y: 100, zoom: 2 });
新 API
const { setViewport, setCenter, setZoom } = useReactFlow();
...
setViewport({ x: 100, y: 100, zoom: 2 });
新的视口函数
getZoom
getViewport
7. isHidden - hidden
我们混合了带前缀 (is...
) 和不带前缀的布尔选项名称。所有节点和边选项现在都不带前缀。因此它是 hidden
, animated
, selected
, draggable
, selectable
和 connectable
。
旧 API
const hiddenNode = { id: '1', isHidden: true, position: { x: 50, y: 50 } };
新 API
const hiddenNode = { id: '1', hidden: true, position: { x: 50, y: 50 } };
8. arrowHeadType markerEndId - markerStart / markerEnd
我们改进了用于自定义边标记的 API。使用新 API,您可以在边的开头和结尾设置单独的标记,以及使用颜色、strokeWidth 等对其进行自定义。您仍然可以设置 markerEndId,但现在不再使用不同的属性,markerStart
和 markerEnd
属性接受字符串(您需要自己定义的 svg 标记的 ID)或用于使用内置 arrowclosed 或 arrow 标记的配置对象。
旧 API
const markerEdge = { source: '1', target: '2', arrowHeadType: 'arrow' };
新 API
const markerEdge = {
source: '1',
target: '2',
markerStart: 'myCustomSvgMarker',
markerEnd: { type: 'arrow', color: '#f00' },
};
9. ArrowHeadType - MarkerType
这只是为了使标记 API 更加一致而进行的措辞更改。由于我们现在能够为边的开头设置标记,因此 ArrowHeadType 类型已重命名为 MarkerType。将来,这不仅可以包含箭头形状,还可以包含其他形状,例如圆形、菱形等。
10. 属性
这不是对 API 的真正重大更改,而是在 React Flow 的整体外观上进行了一点更改。我们在右下角添加了一个很小的“React Flow”属性(位置可以通过 attributionPosition
属性配置)。此更改与新的“React Flow Pro”订阅模式一起提供。如果您想在商业应用程序中删除属性,请订阅 “React Flow Pro”。