學習疑難排解

遷移至 v10

您可以在這裡找到舊版 React Flow 的文件: v11, v10, v9

歡迎使用 React Flow v10!隨著主要版本更新,將會推出許多新功能,但也有些重大變更。

新功能

  • 子流程圖:您現在可以將節點新增至父節點,並建立群組和巢狀流程圖
  • 節點類型「群組」:一種沒有控制柄的新節點類型,可以用作群組節點
  • 觸控裝置支援:現在可以在觸控裝置上連接節點
  • 初始化時符合視窗大小:您可以使用新的 fitView 屬性來調整初始視窗大小
  • 按鍵處理:現在不僅可以處理單個按鍵,還可以處理多個按鍵和按鍵組合
  • useKeyPress hook:用於處理鍵盤事件的實用 hook
  • useReactFlow hook:回傳一個 React Flow 實例,該實例會公開操作流程圖的函式
  • useNodesuseEdgesuseViewport hook:用於接收節點、邊線和視窗的 hook
  • 邊線標記:更多選項可設定邊線的起始和結束標記

重大變更

TLDR

  • elements 陣列拆分為 nodesedges 陣列,並實作 onNodesChangeonEdgesChange 處理常式(詳細指南請見核心概念章節
  • 記憶您的自訂 nodeTypesedgeTypes
  • onLoad 重新命名為 onInit
  • paneMoveable 重新命名為 panOnDrag
  • useZoomPanHelper 重新命名為 useReactFlow (並將 setTransform 重新命名為 setViewport
  • 將節點和邊線選項 isHidden 重新命名為 hidden

重大變更的詳細說明

1. 元素 - 節點和邊線

我們發現很多人在使用半受控的 elements 屬性時遇到困難。將本機使用者狀態與 React Flow 的內部狀態同步始終有點混亂。你們有些人使用了從未記錄的內部儲存,而且始終是一種有點 hacky 的解決方案。在新版本中,我們提供兩種使用 React Flow 的方式 - 非受控和受控。

1.1. 受控的 nodesedges

如果您想要完全控制,並從本機狀態或儲存中使用節點和邊線,您可以使用 nodesedges 屬性以及 onNodesChangeonEdgesChange 處理常式。您需要實作這些處理常式才能建立互動式流程圖(如果只想要平移和縮放,則不需要它們)。當節點初始化、拖曳、選取或移除時,您會收到變更。這表示您始終知道節點的確切位置和尺寸,或者是否已選取節點。我們匯出輔助函式 applyNodeChangesapplyEdgeChanges,您應該使用這些函式來套用變更。

舊 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;

您也可以使用新的 hook useNodesStateuseEdgesState 來快速開始

const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

相關變更

  • onElementsClick ->onNodeClickonEdgeClick
  • onElementsRemove -> 由 onNodesChangeonEdgesChange 處理常式取代

1.2 無控制的 defaultNodesdefaultEdges

最簡單的入門方式是使用 defaultNodesdefaultEdges 屬性。當您設定這些屬性時,所有操作都會在內部處理。您不需要新增任何其他處理器即可獲得一個完全互動的流程,其中包含拖曳節點、連接節點以及移除節點和邊線的功能。

新的 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 hook 或使用 onInit 處理器來接收該實例,該處理器會將實例作為函數參數傳遞。

2. 記憶您的自訂 nodeTypesedgeTypes

每當您傳遞新的節點或邊線類型時,我們會在背景中建立包裝的節點或邊線元件類型。這表示您不應在每次渲染時都建立新的 nodeTypeedgeType 物件。記憶您的 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。透過此變更,我們可以從我們的狀態相關程式碼中移除約 300 行程式碼。如果您需要存取內部儲存,您可以使用 useStore hook

舊的 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 函式都已移至從 useReactFlow hookonInit 處理器取得的 React Flow 實例中。

舊的 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...) 和無前綴的布林選項名稱。所有節點和邊線選項都不再有前綴。所以現在是 hiddenanimatedselecteddraggableselectableconnectable

舊的 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,但 markerStartmarkerEnd 屬性不再使用不同的屬性,而是接受一個字串 (您需要自行定義的 svg 標記的 id) 或一個用於使用內建 arrowclosed 或箭頭標記的設定物件。

舊的 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」