學習疑難排解

疑難排解

本指南包含使用 React Flow 時可能發生的警告和錯誤。我們也加入了從我們的 Discord 伺服器Github 問題Github 討論中收集的常見問題和陷阱。

警告:看起來您沒有使用 zustand 提供器作為祖先

這通常發生在

A:您安裝了兩個不同版本的 @reactflow/core。
B:您正在嘗試在 React Flow 內容之外存取內部 React Flow 狀態。

A 的解決方案

更新 reactflow 和 @reactflow/node-resizer (如果您正在使用),移除 node_modules 和 package-lock.json 並重新安裝依賴項。

B 的解決方案

可能的解決方案是使用 <ReactFlowProvider /> 包裹您的元件,或將存取狀態的程式碼移至您的 React Flow 實例的子元件內。

🚫
這會導致錯誤
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
function FlowWithoutProvider(props) {
  // cannot access the state here
  const reactFlowInstance = useReactFlow();
 
  return <ReactFlow {...props} />;
}
 
export default FlowWithoutProvider;
🚫
這也會導致錯誤
import { ReactFlow, ReactFlowProvider } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
function Flow(props) {
  // still cannot access the state here
  // only child components of this component can access the state
  const reactFlowInstance = useReactFlow();
 
  return (
    <ReactFlowProvider>
      <ReactFlow {...props} />
    </ReactFlowProvider>
  );
}
 
export default FlowWithProvider;
這樣做可以

當您想要存取 React Flow 的內部狀態時 (例如使用 useReactFlow hook),您需要使用 <ReactFlowProvider /> 包裹您的元件。這裡的包裹是在元件之外完成的

import { ReactFlow, ReactFlowProvider } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
function Flow(props) {
  // you can access the internal state here
  const reactFlowInstance = useReactFlow();
 
  return <ReactFlow {...props} />;
}
 
// wrapping with ReactFlowProvider is done outside of the component
function FlowWithProvider(props) {
  return (
    <ReactFlowProvider>
      <Flow {...props} />
    </ReactFlowProvider>
  );
}
 
export default FlowWithProvider;

看起來您建立了一個新的 nodeTypes 或 edgeTypes 物件。如果這不是

有意的,請在元件之外定義 nodeTypes/edgeTypes 或將其記憶化。

nodeTypesedgeTypes 屬性在初始渲染後發生變更時,會出現此警告。nodeTypesedgeTypes 應該只在極少數情況下動態變更。通常,它們會使用您應用程式中使用的所有類型定義一次。很容易發生您在元件渲染函式中定義 nodeTypes 或 edgeTypes 物件,這會導致 React Flow 在元件每次重新渲染時重新渲染。

🚫
導致警告
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
import MyCustomNode from './MyCustomNode';
 
function Flow(props) {
  // new object being created on every render
  // causing unneccessary re-renders
  const nodeTypes = {
    myCustomNode: MyCustomNode,
  };
 
  return <ReactFlow nodeTypes={nodeTypes} />;
}
 
export default Flow;
建議的實作
import { ReactFlow } from '@xyflow/react';
import MyCustomNode from './MyCustomNode';
 
// defined outside of the component
const nodeTypes = {
  myCustomNode: MyCustomNode,
};
 
function Flow(props) {
  return <ReactFlow nodeTypes={nodeTypes} />;
}
 
export default Flow;
替代實作

如果您想要動態變更 nodeTypes 而不會導致不必要的重新渲染,可以使用此方法。

import { useMemo } from 'react';
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
import MyCustomNode from './MyCustomNode';
 
function Flow(props) {
  const nodeTypes = useMemo(
    () => ({
      myCustomNode: MyCustomNode,
    }),
    [],
  );
 
  return <ReactFlow nodeTypes={nodeTypes} />;
}
 
export default Flow;

找不到節點類型。正在使用後備類型「default」。

當您為其中一個節點指定自訂節點類型,但沒有將正確的 nodeTypes 屬性傳遞給 React Flow 時,通常會發生這種情況。自訂節點的 type 選項的字串需要與 nodeTypes 物件的鍵完全相同。

🚫
不起作用
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
import MyCustomNode from './MyCustomNode';
 
const nodes = [
  {
    id: 'mycustomnode',
    type: 'custom',
    // ...
  },
];
 
function Flow(props) {
  // nodeTypes property is missing, so React Flow cannot find the custom node component to render
  return <ReactFlow nodes={nodes} />;
}
🚫
這樣也不起作用
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
import MyCustomNode from './MyCustomNode';
 
const nodes = [
  {
    id: 'mycustomnode',
    type: 'custom',
    // ...
  },
];
 
const nodeTypes = {
  Custom: MyCustomNode,
};
 
function Flow(props) {
  // node.type and key in nodeTypes object are not exactly the same (capitalized)
  return <ReactFlow nodes={nodes} nodeTypes={nodeTypes} />;
}
這樣做可以
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
import MyCustomNode from './MyCustomNode';
 
const nodes = [
  {
    id: 'mycustomnode',
    type: 'custom',
    // ...
  },
];
 
const nodeTypes = {
  custom: MyCustomNode,
};
 
function Flow(props) {
  return <ReactFlow nodes={nodes} nodeTypes={nodeTypes} />;
}

React Flow 父容器需要寬度和高度才能渲染圖形。

在幕後,React Flow 會測量父 DOM 元素以調整渲染器。如果您嘗試在沒有高度的常規 div 中渲染 React Flow,我們將無法顯示圖形。如果您遇到此警告,您需要確保您的包裝元件附加了一些 CSS,以便它獲得固定高度或繼承其父元素的高度。

🚫
這將導致警告
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
function Flow(props) {
  return (
    <div>
      <ReactFlow {...props} />
    </div>
  );
}
運作範例
import { ReactFlow } from '@xyflow/react';
 
function Flow(props) {
  return (
    <div style={{ height: 800 }}>
      <ReactFlow {...props} />
    </div>
  );
}

只有子節點可以使用父範圍。

當您嘗試將 extent 選項新增至沒有父節點的節點時,會出現此警告。根據您嘗試執行的操作,您可以移除 extent 選項或指定 parentNode

🚫
會顯示警告
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
const nodes = [
  {
    id: 'mycustomnode',
    extent: 'parent',
    // ...
  },
];
 
function Flow(props) {
  return <ReactFlow nodes={nodes} />;
}
警告已解決
const nodes = [
  {
    id: 'mycustomnode',
    parentNode: 'someothernode',
    extent: 'parent',
    // ...
  },
];
 
function Flow(props) {
  return <ReactFlow nodes={nodes} />;
}

無法建立邊。邊需要一個來源和一個目標。

當您沒有將 sourcetarget 選項傳遞給邊物件時,就會發生這種情況。沒有來源和目標,就無法渲染邊。

🚫
將顯示警告
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
 
const nodes = [
  /* ... */
];
 
const edges = [
  {
    nosource: '1',
    notarget: '2',
  },
];
 
function Flow(props) {
  return <ReactFlow nodes={nodes} edges={edges} />;
}
這樣做可以
import { ReactFlow } from '@xyflow/react';
 
const nodes = [
  /* ... */
];
 
const edges = [
  {
    source: '1',
    target: '2',
  },
];
 
function Flow(props) {
  return <ReactFlow nodes={nodes} edges={edges} />;
}

舊的 id="some-id" 邊不存在。

當您嘗試重新連接邊,但您要更新的邊已經從狀態中移除時,可能會發生這種情況。這是一個非常罕見的情況。請參閱重新連接邊範例,以了解實作詳細資料。

無法為來源/目標手柄 id:「some-id」建立邊;邊 id:「some-id」。

如果您正在使用多個手柄,且手柄的 id 屬性找不到手柄,或者您在以程式方式新增或移除手柄後沒有更新節點內部,就會發生這種情況。請參閱自訂節點範例,以了解使用多個手柄的範例。

標記類型不存在。

當您嘗試指定 React Flow 中未內建的標記類型時,會出現此警告。現有的標記類型已記錄於此處

Handle:找不到節點 ID。

當您嘗試在自訂節點元件之外使用 <Handle /> 元件時,會出現此警告。

當我使用 webpack 4 建置應用程式時,出現錯誤。

如果您使用 webpack 4,您可能會遇到類似這樣的錯誤

ERROR in /node_modules/@reactflow/core/dist/esm/index.js 16:19
Module parse failed: Unexpected token (16:19)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.dev.org.tw/concepts#loaders

React Flow 是一個現代化的 JavaScript 程式碼庫,使用了許多較新的 JavaScript 功能。預設情況下,webpack 4 不會轉換您的程式碼,並且它不知道如何處理 React Flow。

您需要將一些 babel 外掛程式新增至您的 webpack 設定中,才能使其正常運作

$ npm i --save-dev babel-loader@8.2.5 @babel/preset-env @babel/preset-react @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-nullish-coalescing-operator

並像這樣設定 loader

{
  test: /node_modules[\/\\]@?reactflow[\/\\].*.js$/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env', "@babel/preset-react"],
      plugins: [
        "@babel/plugin-proposal-optional-chaining",
        "@babel/plugin-proposal-nullish-coalescing-operator",
      ]
    }
  }
}

如果您使用 webpack 5,則不需要執行任何操作!React Flow 將開箱即用。

當我的節點包含 <canvas /> 元素時,滑鼠事件無法一致地運作。

如果您在自訂節點內使用 <canvas /> 元素,您可能會在 cavnas 中的滑鼠事件中遇到看似不正確的坐標問題。

React Flow 使用 CSS 轉換來縮放節點,當您放大和縮小時。然而,從 DOM 的角度來看,元素的大小仍然相同。如果您有想要計算滑鼠相對於 canvas 元素的位置的事件監聽器,這可能會導致問題。

為了在您控制的事件處理程序中解決此問題,您可以將計算出的相對位置縮放 1 / zoom,其中 zoom 是 flow 的當前縮放級別。要獲取當前縮放級別,您可以使用 useReactFlow hook 中的 getZoom 方法。