迁移到 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”。