遷移至 v10
歡迎使用 React Flow v10!隨著主要版本更新,將會推出許多新功能,但也有些重大變更。
新功能
- 子流程圖:您現在可以將節點新增至父節點,並建立群組和巢狀流程圖
- 節點類型「群組」:一種沒有控制柄的新節點類型,可以用作群組節點
- 觸控裝置支援:現在可以在觸控裝置上連接節點
- 初始化時符合視窗大小:您可以使用新的
fitView
屬性來調整初始視窗大小 - 按鍵處理:現在不僅可以處理單個按鍵,還可以處理多個按鍵和按鍵組合
- useKeyPress hook:用於處理鍵盤事件的實用 hook
- useReactFlow hook:回傳一個 React Flow 實例,該實例會公開操作流程圖的函式
- useNodes、useEdges 和 useViewport hook:用於接收節點、邊線和視窗的 hook
- 邊線標記:更多選項可設定邊線的起始和結束標記
重大變更
TLDR
- 將
elements
陣列拆分為nodes
和edges
陣列,並實作onNodesChange
和onEdgesChange
處理常式(詳細指南請見核心概念章節) - 記憶您的自訂
nodeTypes
和edgeTypes
- 將
onLoad
重新命名為onInit
- 將
paneMoveable
重新命名為panOnDrag
- 將
useZoomPanHelper
重新命名為useReactFlow
(並將setTransform
重新命名為setViewport
) - 將節點和邊線選項
isHidden
重新命名為hidden
重大變更的詳細說明
1. 元素 - 節點和邊線
我們發現很多人在使用半受控的 elements
屬性時遇到困難。將本機使用者狀態與 React Flow 的內部狀態同步始終有點混亂。你們有些人使用了從未記錄的內部儲存,而且始終是一種有點 hacky 的解決方案。在新版本中,我們提供兩種使用 React Flow 的方式 - 非受控和受控。
1.1. 受控的 nodes
和 edges
如果您想要完全控制,並從本機狀態或儲存中使用節點和邊線,您可以使用 nodes
、edges
屬性以及 onNodesChange
和 onEdgesChange
處理常式。您需要實作這些處理常式才能建立互動式流程圖(如果只想要平移和縮放,則不需要它們)。當節點初始化、拖曳、選取或移除時,您會收到變更。這表示您始終知道節點的確切位置和尺寸,或者是否已選取節點。我們匯出輔助函式 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;
您也可以使用新的 hook 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
hook 或使用 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。透過此變更,我們可以從我們的狀態相關程式碼中移除約 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
hook 或 onInit
處理器取得的 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...
) 和無前綴的布林選項名稱。所有節點和邊線選項都不再有前綴。所以現在是 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 或箭頭標記的設定物件。
舊的 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」。