2024/01/20
React Flow UI 入门


2025 年 11 月更新:我们已更新教程,以使用最新版本的 shadcn/ui,以及 React 19 和 Tailwind 4!
¥Update November 2025: We have updated the tutorial to use the latest version of shadcn/ui, on React 19 and Tailwind 4!
2025 年 7 月更新:“React Flow UI” 以前称为 “React Flow 组件”。我们重命名它,因为它现在包含组件和模板。此外,由于它基于 shadcn/ui 构建,“UI” 的命名方式使开发者更容易识别两者之间的联系并理解我们提供的功能。
¥Update July 2025: “React Flow UI” was formerly known as “React Flow Components”. We renamed it because it now includes both components and templates. Additionally, since it’s built on shadcn/ui, the “UI” naming makes it easier for developers to recognize the connection and understand what we offer.
最近,我们在开源名册中推出了一个令人兴奋的新成员:React Flow UI(以前称为 React Flow Components)。这些是预先构建的节点、边和其他 UI 元素,你可以快速将它们添加到你的 React Flow 应用中以启动并运行。问题是这些组件建立在 shadcn/ui 和 shadcn CLI 之上。
¥Recently, we launched an exciting new addition to our open-source roster: React Flow UI (Previously known as React Flow Components). These are pre-built nodes, edges, and other ui elements that you can quickly add to your React Flow applications to get up and running. The catch is these components are built on top of shadcn/ui and the shadcn CLI.
我们之前写过我们的经验以及是什么促使我们在 xyflow 博客 上选择 shadcn,但在本教程中,我们将重点介绍如何从头开始使用 shadcn、Tailwind CSS 和 React Flow Components。
¥We’ve previously written about our experience and what led us to choosing shadcn over on the xyflow blog , but in this tutorial we’re going to focus on how to get started from scratch with shadcn, Tailwind CSS, and React Flow Components.
等等,shadcn 是什么?
¥Wait, what’s shadcn?
不,是谁!Shadcn 是一组称为 shadcn/ui 的预先设计的组件的作者。请注意,我们在那里没有说库?Shadcn 采用不同的方法,将组件添加到项目的源代码中并由你进行 “owned”:添加组件后,你可以随意修改它以满足你的需求!
¥No what, who! Shadcn is the author of a collection of pre-designed components known as
shadcn/ui. Notice how we didn’t say library there? Shadcn takes a different approach
where components are added to your project’s source code and are “owned” by you: once you
add a component you’re free to modify it to suit your needs!
入门
¥Getting started
首先,我们将:
¥To begin with, we will:
-
设置一个新的
vite项目。¥Set up a new
viteproject. -
设置 shadcn/ui 和 Tailwind CSS 。
¥Set up shadcn/ui along with Tailwind CSS .
-
添加和配置 React Flow。
¥Add and configure React Flow.
-
使用 UI 组件注册表 中的构建块创建我们自定义的 React Flow 组件。
¥Create our custom React Flow components using the building blocks in our UI components registry.
设置新的 Vite 项目
¥Setting up a new vite project
npm create vite@latestVite 能够为许多流行框架搭建项目,但我们只关心 React!此外,请确保设置 TypeScript 项目。React Flow 的文档是 JavaScript 和 TypeScript 的混合体,但对于 shadcn 组件,TypeScript 是必需的!
¥Vite is able to scaffold projects for many popular frameworks, but we only care about React! Additionally, make sure to set up a TypeScript project. React Flow’s documentation is a mix of JavaScript and TypeScript, but for shadcn components TypeScript is required!
在交互式设置过程中,选择 React 和 TypeScript:
¥During the interactive setup, select React and TypeScript:
◇ Project name:
│ my-react-flow-app
│
◇ Select a framework:
│ React
│
◇ Select a variant:
│ TypeScript
│
◇ Use rolldown-vite (Experimental)?:
│ No
│
◇ Install with pnpm and start now?
│ Yes
│
◇ Scaffolding project in /Users/alessandro/src/xyflow/wip/component-style-test-2...
│
◇ Installing dependencies with pnpm...设置 Tailwind CSS
¥Setting up Tailwind CSS
所有 shadcn 和 React Flow 组件都使用 Tailwind CSS 进行样式设置,因此接下来我们需要安装 Tailwind CSS 和其他一些依赖。
¥All shadcn and React Flow components are styled with Tailwind CSS , so we’ll need to install that and a few other dependencies next.
我们可以按照 shadcn 安装指南 中的说明,在新创建的 Vite 项目中安装 shadcn 和 Tailwind CSS。
¥We can follow the instructions in the shadcn installation guide to install shadcn and Tailwind CSS inside of a freshly scaffolded vite project.
npm install tailwindcss @tailwindcss/vite现在在 Vite 项目中设置 Tailwind CSS 变得更加简单,Tailwind 4 完全使用 CSS 进行配置。你只需将生成的 src/index.css 文件替换为以下一行代码:
¥It is now a lot simpler to set up Tailwind CSS in a vite project, and Tailwind 4 is configured completely in CSS.
You can just replace the generated src/index.css file with this one line:
@import "tailwindcss";将 Tailwind CSS 作为 Vite 插件导入
¥Importing Tailwind CSS as a Vite plugin
从 Tailwind CSS v4 开始,你可以使用专用的 Vite 插件 @tailwindcss/vite,而不是传统的 PostCSS 插件。此插件已在我们的 vite.config.ts 文件中配置,大大简化了开发者和编译器的工作。
¥Starting with Tailwind CSS v4 , you can use the dedicated Vite plugin @tailwindcss/vite
rather than the traditional PostCSS plugin. This plugin is configured in our vite.config.ts file, and makes
things a lot simpler, both for us developers, and for the compilers.
我们只需导入插件并将其添加到 vite.config.ts 文件中的 plugins 数组即可。我们还需要将 alias 属性添加到 resolve 对象中,以告诉 Vite 源文件的位置,因为 shadcn 组件使用 @ 别名来引用 src 目录。
¥We simply need to import the plugin and add it to the plugins array in our vite.config.ts file.
We also need to add the alias property to the resolve object to tell Vite where to find our source files,
as shadcn components use the @ alias to refer to the src directory.
import path from "path"
import tailwindcss from "@tailwindcss/vite"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})导入 Tailwind CSS 文件
¥Importing the Tailwind CSS file
现在我们需要确保项目中唯一的 CSS 文件是 Tailwind CSS 文件。在生成的 App.tsx 文件中,你可以安全地移除 App.css 文件的导入,并移除脚手架生成的 App.tsx 文件中的所有其他内容。
¥We now need to make sure that the only CSS file in our project is the Tailwind CSS file.
In the generated App.tsx, you can safely remove the import of the App.css file, and
remove everything else that is in the scaffolded App.tsx file.
为了验证 Tailwind CSS 是否正常工作,我们可以添加带有 Tailwind 类的简单 div 和 h1 元素。
¥To verify that Tailwind CSS is working, we can add a simple div and h1 elements with Tailwind classes.
更新后的 App.tsx 文件应如下所示:
¥The updated App.tsx file should look like this:
export function App() {
return (
<div className="w-screen h-screen p-8">
<h1 className="text-2xl font-bold">Hello World</h1>
</div>
);
}
export default App;main.tsx 文件应该如下所示:
¥And, the main.tsx file should look like this:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)如果你已更新 index.css 文件并配置 Vite 使用 Tailwind CSS 插件,你应该能够运行项目并在浏览器中看到 “Hello World” 消息,字体会以醒目的大号粗体显示。
¥If you updated your index.css file and configured Vite to use the Tailwind CSS
plugin, you should be able to run the project and see the “Hello World” message
in your browser, in a nice, large, bold font.
类 w-screen 和 h-screen 是 Tailwind 实用程序类的两个示例。如果你习惯使用不同的方法设计 React 应用,那么一开始你可能会觉得这有点奇怪。你可以将 Tailwind 类视为增强的内联样式:它们受限于一套设计系统,你可以访问响应式媒体查询或伪类,如 hover 和 focus。
¥The classes w-screen and h-screen are two examples of Tailwind’s utility classes. If
you’re used to styling React apps using a different approach, you might find this a bit
strange at first. You can think of Tailwind classes as supercharged inline styles:
they’re constrained to a set design system and you have access to responsive media
queries or pseudo-classes like hover and focus.
设置 shadcn/ui
¥Setting up shadcn/ui
Vite 在生成 TypeScript 项目时为我们搭建了一些 tsconfig 文件,我们需要对这些文件进行一些更改,以便 shadcn 组件可以正常工作。shadcn CLI 非常聪明(我们稍后会讲到),但它不能解释每个项目结构,因此相互依赖的 shadcn 组件会使用 TypeScript 的导入路径。
¥Vite scaffolds some tsconfig files for us when generating a TypeScript project and we’ll
need to make some changes to these so the shadcn components can work correctly. The shadcn
CLI is pretty clever (we’ll get to that in a second) but it can’t account for every
project structure so instead shadcn components that depend on one another make use of
TypeScript’s import paths.
当前版本的 Vite 将 TypeScript 配置拆分为三个文件,其中两个需要编辑。将 baseUrl 和 paths 属性添加到 tsconfig.json 和 tsconfig.app.json 文件的 compilerOptions 部分:
¥The current version of Vite splits TypeScript configuration into three files,
two of which need to be edited. Add the baseUrl and paths properties to the compilerOptions section of the
tsconfig.json and tsconfig.app.json files:
{
// ...
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
// ...
}{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
// ...
}
}很好!现在我们准备设置 shadcn/ui CLI 并添加我们的第一个组件。CLI 设置完成后,我们将能够使用单个命令向项目中添加新组件。 - 即使它们具有依赖或需要修改现有文件!
¥Nice! Now we’re ready to set up the shadcn/ui CLI and add our first
components. Once the CLI is set up, we’ll be able to add new components to our
project with a single command - even if they have dependencies or need to modify
existing files!
我们现在可以运行以下命令来在我们的项目中设置 shadcn/ui:
¥We can now run the following command to set up shadcn/ui in our project:
npx shadcn@latest initCLI 将执行几个任务,首先它会识别你项目的框架和 Tailwind 版本,然后询问你希望使用哪种颜色作为项目的基础颜色。它将更新你的 index.css 文件,并在项目根目录生成一个 components.json 文件,这将是 shadcn 的主要配置点。
¥The CLI will perform a few tasks, first it will identify your project’s
framework, tailwind version, and then ask you what color you would like to use
as the base color for your project. It will then update your index.css file and generate a components.json file in the
root of your project, which will be shadcn’s main configuration points.
目前我们可以使用所有默认选项。
¥We can take all the default options for now
✔ Preflight checks.
✔ Verifying framework. Found Vite.
✔ Validating Tailwind CSS config. Found v4.
✔ Validating import alias.
✔ Which color would you like to use as the base color? › Neutral
✔ Writing components.json.
✔ Checking registry.
✔ Updating CSS variables in src/index.css
✔ Installing dependencies.
✔ Created 1 file:
- src/lib/utils.ts
Success! Project initialization completed.
You may now add components.安装 React Flow 并导入其 CSS
¥Installing React Flow and importing its CSS.
现在我们可以安装 React Flow 并导入其 CSS。
¥Now we can install React Flow and import its CSS.
npm install @xyflow/react然后将 Tailwind 的 CSS 导入到我们的 App.tsx 文件中:
¥And then import its CSS in our App.tsx file:
import '@xyflow/react/dist/style.css';
export function App() {
return (
<div className="w-screen h-screen p-8">
<h1 className="text-2xl font-bold">Hello World</h1>
</div>
);
}
export default App;添加你的第一个组件
¥Adding your first components
为了展示 shadcn 的强大功能,让我们直接开始制作一个新的 React Flow 应用!现在一切都已设置好,我们可以使用单个命令添加 <BaseNode /> 组件:
¥To demonstrate how powerful shadcn can be, let’s dive right into making a new React
Flow app! Now everything is set up, we can add the
<BaseNode /> component with a single command:
npx shadcn@latest add https://ui.reactflow.dev/base-node此命令将生成一个新的 src/components/base-node.tsx 文件,并安装必要的依赖。
¥This command will generate a new file src/components/base-node.tsx, and install the necessary dependencies.
该 <BaseNode /> 组件不是直接的 React Flow 节点。相反,顾名思义,它是我们许多其他节点的基础。它还附带其他组件,可用于为节点提供标题和内容。这些组件是:
¥That <BaseNode /> component is not a React Flow node directly. Instead, as the name
implies, it’s a base that many of our other nodes build upon. It also comes with
additional components that you can use to provide a header and content for your nodes.
These components are:
-
<BaseNodeHeader /> -
<BaseNodeHeaderTitle /> -
<BaseNodeContent /> -
<BaseNodeFooter />
你也可以使用它为所有节点设置统一的样式。让我们通过更新我们的 App.tsx 文件来查看它是什么样子:
¥You can use it to have a unified style for all of your nodes as well. Let’s see what it
looks like by updating our App.tsx file:
import '@xyflow/react/dist/style.css';
import {
BaseNode,
BaseNodeContent,
BaseNodeHeader,
BaseNodeHeaderTitle,
} from "@/components/base-node";
function App() {
return (
<div className="w-screen h-screen p-8">
<BaseNode>
<BaseNodeHeader>
<BaseNodeHeaderTitle>Base Node</BaseNodeHeaderTitle>
</BaseNodeHeader>
<BaseNodeContent>
This is a base node component that can be used to build other nodes.
</BaseNodeContent>
</BaseNode>
</div>
);
}
export default App;好吧,不是特别令人兴奋…
¥Ok, not super exciting…

<BaseNode /> 组件是我们 UI 组件注册表 中最常用的组件之一。某些组件可能在内部使用它来创建具有一致样式的自定义节点,而其他一些组件可以与它结合使用来创建更复杂的节点。
¥The <BaseNode /> component is one of the most used components in our UI components registry.
Some components may use it internally, to create custom nodes with a consistent style,
while some other components can be used in combination with it to create more complex nodes.
例如,让我们将 <NodeTooltip /> 组件添加到项目中,以便在鼠标悬停在节点上时显示工具提示。
¥For example, let’s add the <NodeTooltip /> component to our project, to display a tooltip when hovering over a node.
npx shadcn@latest add https://ui.reactflow.dev/node-tooltip我们将更新我们的 App.tsx 文件以渲染正确的流程。我们将使用与大多数示例相同的基本设置,因此我们不会在这里分解各个部分。如果你对 React Flow 还不熟悉,并且想进一步了解如何从头开始设置基本流程,请查看我们的 快速入门指南。
¥And we’ll update our App.tsx file to render a proper flow. We’ll use the same basic
setup as most of our examples so we won’t break down the individual pieces here. If you’re
still new to React Flow and want to learn a bit more about how to set up a basic flow from
scratch, check out our quickstart guide.
import { Position, ReactFlow, useNodesState, type Node } from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { BaseNode, BaseNodeContent } from "@/components/base-node";
import {
NodeTooltip,
NodeTooltipContent,
NodeTooltipTrigger,
} from "@/components/node-tooltip";
function Tooltip() {
return (
<NodeTooltip>
<NodeTooltipContent position={Position.Top}>
Hidden Content
</NodeTooltipContent>
<BaseNode>
<BaseNodeContent>
<NodeTooltipTrigger>Hover</NodeTooltipTrigger>
</BaseNodeContent>
</BaseNode>
</NodeTooltip>
);
}
const nodeTypes = {
tooltip: Tooltip,
};
const initialNodes: Node[] = [
{
id: "1",
position: { x: 0, y: 0 },
data: {},
type: "tooltip",
},
];
function Flow() {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
return (
<div className="h-screen w-screen p-8 bg-gray-50 rounded-xl">
<ReactFlow
nodes={nodes}
nodeTypes={nodeTypes}
onNodesChange={onNodesChange}
fitView
/>
</div>
);
}
export default function App() {
return <Flow />;
}你看,我们添加的工具提示节点自动使用了我们自定义的 <BaseNode /> 组件!
¥And would you look at that, the tooltip node we added automatically uses the
<BaseNode /> component we customized!
快速行动并创造事物
¥Moving fast and making things
现在我们对 shadcn/ui 和 CLI 的工作原理有了基本的了解,我们可以开始看到添加新组件和构建流程是多么容易。要查看 React Flow Components 提供的所有内容,让我们构建一个简单的计算器流程。
¥Now we’ve got a basic understanding of how shadcn/ui and the CLI works, we can begin to see how easy it is to add new components and build out a flow. To see everything React Flow Components has to offer let’s build out a simple calculator flow.
首先,让我们删除 <NodeTooltip /> 并撤消对 <BaseNode /> 的更改。除了预制节点外,React Flow UI 还包含用于创建自定义节点的构建块。为了查看它们,我们将添加 labeled-handle 组件:
¥First let’s remove the <NodeTooltip /> and undo our changes to <BaseNode />. In
addition to pre-made nodes, React Flow UI also contains building blocks for creating your
own custom nodes. To see them, we’ll add the labeled-handle component:
npx shadcn@latest add https://ui.reactflow.dev/labeled-handle数字节点
¥The Number Node
我们将创建的第一个节点是一个简单的数字节点,带有一些按钮来增加和减少值以及一个将其连接到其他节点的句柄。创建文件夹 src/components/nodes,然后添加新文件 src/components/nodes/num-node.tsx。
¥The first node we’ll create is a simple number node with some buttons to increment and
decrement the value and a handle to connect it to other nodes. Create a folder
src/components/nodes and then add a new file src/components/nodes/num-node.tsx.
我们需要安装以下 shadcn/ui 组件:
¥We need to install the following shadcn/ui components:
npx shadcn@latest add dropdown-menu button现在我们可以开始构建节点了。我们需要从 useReactFlow 钩子中访问 updateNodeData 函数来更新节点数据,并访问 setNodes 函数来删除节点。该钩子帮助我们创建可在应用其他部分使用的独立组件,同时仍然能够让我们快速访问 React Flow 的状态和函数。
¥Now we can start building the node. We will need to access the updateNodeData
function to update the node’s data and the setNodes function to delete the
node, from the useReactFlow hook. The hook helps us make self-contained
components that can be used in other parts of our application, while still
giving us quick access to React Flow’s state and functions.
我们需要创建四个回调函数来处理可以对节点执行的不同操作。
¥We will need to make four callbacks, to handle the different actions that can be performed on the node.
-
将节点值重置为 0
¥Reset the node’s value to 0
-
删除节点
¥Delete the node
-
将节点的值加 1
¥Increment the node’s value by 1
-
将节点的值减 1
¥Decrement the node’s value by 1
我们还需要访问节点的数据以获取当前值并进行更新。
¥We will also need to access the node’s data to get the current value and update it.
import { type Node, type NodeProps, Position, useReactFlow } from '@xyflow/react';
import { useCallback } from 'react';
import {
BaseNode,
BaseNodeContent,
BaseNodeFooter,
BaseNodeHeader,
BaseNodeHeaderTitle,
} from '../base-node';
import { LabeledHandle } from '../labeled-handle';
import { EllipsisVertical } from 'lucide-react';
import { Button } from '../ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from '../ui/dropdown-menu';
export type NumNode = Node<{
value: number;
}>;
export function NumNode({ id, data }: NodeProps<NumNode>) {
const { updateNodeData, setNodes } = useReactFlow();
const handleReset = useCallback(() => {
updateNodeData(id, { value: 0 });
}, [id, updateNodeData]);
const handleDelete = useCallback(() => {
setNodes((nodes) => nodes.filter((node) => node.id !== id));
}, [id, setNodes]);
const handleIncr = useCallback(() => {
updateNodeData(id, { value: data.value + 1 });
}, [id, data.value, updateNodeData]);
const handleDecr = useCallback(() => {
updateNodeData(id, { value: data.value - 1 });
}, [id, data.value, updateNodeData]);
return (
<BaseNode>
<BaseNodeHeader className="border-b">
<BaseNodeHeaderTitle>Num</BaseNodeHeaderTitle>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="nodrag p-1"
aria-label="Node Actions"
title="节点操作"
>
<EllipsisVertical className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel className="font-bold">Node Actions</DropdownMenuLabel>
<DropdownMenuItem onSelect={handleReset}>Reset</DropdownMenuItem>
<DropdownMenuItem onSelect={handleDelete}>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BaseNodeHeader>
<BaseNodeContent>
<div className="flex gap-2 items-center">
<Button onClick={handleDecr}>-</Button>
<pre>{String(data.value).padStart(3, ' ')}</pre>
<Button onClick={handleIncr}>+</Button>
</div>
</BaseNodeContent>
<BaseNodeFooter className="bg-gray-100 items-end px-0 py-1 w-full rounded-b-md">
<LabeledHandle title="out" type="source" position={Position.Right} />
</BaseNodeFooter>
</BaseNode>
);
}求和节点
¥The Sum Node
我们可以创建的第二个节点是一个简单的求和节点,它将两个输入节点的值相加。创建一个名为 src/components/nodes/sum-node.tsx 的新文件,并将以下内容粘贴到其中:
¥The second node we can create is a simple sum node that adds the values of the two input nodes.
Create a new file src/components/nodes/sum-node.tsx and paste the following into it:
具体来说,我们需要访问 getNodeConnections 函数来获取两个相连输入节点的值,并在 useEffect hook 中使用 updateNodeData 函数,在其中一个输入节点的值发生变化时,将节点数据更新为这两个输入节点值的总和。
¥Particularly, we will need to access the getNodeConnections function to get
the values of the two connected input nodes and the updateNodeData function to
update the node’s data with the sum of the two input nodes inside of a
useEffect hook, whenever one of the values of the input nodes changes.
import {
type Node,
type NodeProps,
Position,
useReactFlow,
useStore,
} from '@xyflow/react';
import { useCallback, useEffect } from 'react';
import {
BaseNode,
BaseNodeContent,
BaseNodeFooter,
BaseNodeHeader,
BaseNodeHeaderTitle,
} from '../base-node';
import { LabeledHandle } from '../labeled-handle';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from '../ui/dropdown-menu';
import { EllipsisVertical } from 'lucide-react';
import { Button } from '../ui/button';
export type SumNode = Node<{
value: number;
}>;
export function SumNode({ id }: NodeProps<SumNode>) {
const { updateNodeData, getNodeConnections, setNodes, setEdges } = useReactFlow();
const { x, y } = useStore((state) => ({
x: getHandleValue(
getNodeConnections({ nodeId: id, handleId: 'x', type: 'target' }),
state.nodeLookup,
),
y: getHandleValue(
getNodeConnections({ nodeId: id, handleId: 'y', type: 'target' }),
state.nodeLookup,
),
}));
const handleDelete = useCallback(() => {
setNodes((nodes) => nodes.filter((node) => node.id !== id));
setEdges((edges) => edges.filter((edge) => edge.source !== id));
}, [id, setNodes, setEdges]);
useEffect(() => {
updateNodeData(id, { value: x + y });
}, [x, y]);
return (
<BaseNode className="w-32">
<BaseNodeHeader className="border-b">
<BaseNodeHeaderTitle>Sum</BaseNodeHeaderTitle>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="nodrag p-1"
aria-label="Node Actions"
title="节点操作"
>
<EllipsisVertical className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel className="font-bold">Node Actions</DropdownMenuLabel>
<DropdownMenuItem onSelect={handleDelete}>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BaseNodeHeader>
<BaseNodeContent className="px-0">
<LabeledHandle title="x" id="x" type="target" position={Position.Left} />
<LabeledHandle title="y" id="y" type="target" position={Position.Left} />
</BaseNodeContent>
<BaseNodeFooter className="bg-gray-100 items-end px-0 py-1 w-full rounded-b-md">
<LabeledHandle title="out" type="source" position={Position.Right} />
</BaseNodeFooter>
</BaseNode>
);
}
function getHandleValue(
connections: Array<{ source: string }>,
lookup: Map<string, Node<any>>,
) {
return connections.reduce((acc, { source }) => {
const node = lookup.get(source)!;
const value = node.data.value;
return typeof value === 'number' ? acc + value : acc;
}, 0);
}数据边
¥The Data Edge
React Flow UI 不仅提供用于构建节点的组件。我们还提供预构建的边缘和其他 UI 元素,你可以将其放入流程中以便快速构建。
¥React Flow UI doesn’t just provide components for building nodes. We also provide pre-built edges and other UI elements you can drop into your flows for quick building.
为了更好地可视化计算器流程中的数据,让我们引入 data-edge 组件。此边缘将源节点数据对象的字段渲染为边缘本身的标签。将 data-edge 组件添加到你的项目中:
¥To better visualize data in our calculator flow, let’s pull in the data-edge component.
This edge renders a field from the source node’s data object as a label on the edge
itself. Add the data-edge component to your project:
npx shadcn@latest add https://ui.reactflow.dev/data-edge<DataEdge /> 组件通过从其源节点的 data 对象中查找字段来工作。我们一直将计算器字段中每个节点的值存储在 "value" 属性中,因此我们将更新 edgeType 对象以包含新的 data-edge,并更新 onConnect 处理程序以创建此类型的新边,确保正确设置边的 data 对象:
¥The <DataEdge /> component works by looking up a field from its source node’s data
object. We’ve been storing the value of each node in our calculator field in a "value"
property so we’ll update our edgeType object to include the new data-edge and we’ll
update the onConnect handler to create a new edge of this type, making sure to set the
edge’s data object correctly:
流程
¥The Flow
现在我们可以将所有内容整合在一起,创建我们的流程。
¥Now we can put everything together and create our flow.
我们将首先定义自定义节点和边的类型,以及将在应用中显示的初始节点和边。
¥We will start by defining the custom node and edge types, and the initial nodes and edges that will be displayed in our app.
import React, { useCallback } from 'react';
import {
ReactFlow,
type Node,
type Edge,
type OnConnect,
addEdge,
useNodesState,
useEdgesState,
} from '@xyflow/react';
import { NumNode } from './components/nodes/num-node';
import { SumNode } from './components/nodes/sum-node';
import { DataEdge } from './components/data-edge';
import '@xyflow/react/dist/style.css';
const nodeTypes = {
num: NumNode,
sum: SumNode,
};
const initialNodes: Node[] = [
{ id: 'a', type: 'num', data: { value: 0 }, position: { x: 0, y: 0 } },
{ id: 'b', type: 'num', data: { value: 0 }, position: { x: 0, y: 200 } },
{ id: 'c', type: 'sum', data: { value: 0 }, position: { x: 300, y: 100 } },
{ id: 'd', type: 'num', data: { value: 0 }, position: { x: 0, y: 400 } },
{ id: 'e', type: 'sum', data: { value: 0 }, position: { x: 600, y: 400 } },
];
const edgeTypes = {
data: DataEdge,
};
const initialEdges: Edge[] = [
{
id: 'a->c',
type: 'data',
data: { key: 'value' },
source: 'a',
target: 'c',
targetHandle: 'x',
},
{
id: 'b->c',
type: 'data',
data: { key: 'value' },
source: 'b',
target: 'c',
targetHandle: 'y',
},
{
id: 'c->e',
type: 'data',
data: { key: 'value' },
source: 'c',
target: 'e',
targetHandle: 'x',
},
{
id: 'd->e',
type: 'data',
data: { key: 'value' },
source: 'd',
target: 'e',
targetHandle: 'y',
},
];
function Flow() {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect: OnConnect = useCallback(
(params) => {
setEdges((edges) =>
addEdge({ type: 'data', data: { key: 'value' }, ...params }, edges),
);
},
[setEdges],
);
return (
<div className="h-screen w-screen p-8 bg-gray-50 rounded-xl">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
fitView
/>
</div>
);
}
export function App() {
return <Flow />;
}将所有内容放在一起,我们最终得到了一个功能强大的小型计算器!
¥Putting everything together we end up with quite a capable little calculator!
你可以通过添加节点来执行其他操作或使用 shadcn/ui 注册表 中的其他组件获取用户输入,从而继续改进此流程。事实上,请密切关注本指南的后续内容,我们将展示使用 React Flow Components 构建的完整应用。
¥You could continue to improve this flow by adding nodes to perform other operations or to take user input using additional components from the shadcn/ui registry . In fact, keep your eyes peeled soon for a follow-up to this guide where we’ll show a complete application built using React Flow Components .
总结
¥Wrapping up
在短短的时间内,我们设法使用 shadcn React Flow Components 提供的组件和构建块构建了一个相当完整的流程。我们已经了解了:
¥In just a short amount of time we’ve managed to build out a fairly complete flow using the components and building blocks provided by shadcn React Flow Components. We’ve learned:
-
如何使用
<BaseNodeHeader />和<LabeledHandle />组件等构建块来构建我们自己的自定义节点,而无需从头开始。¥How to use building blocks like the
<BaseNodeHeader />and<LabeledHandle />components to build our own custom nodes without starting from scratch. -
React Flow UI 还提供了像
<DataEdge />这样的自定义边,可以将其放入我们的应用中。¥That React Flow UI also provides custom edges like the
<DataEdge />to drop into our applications.
得益于 Tailwind 的强大功能,调整这些组件的视觉样式就像编辑 CSS 文件中的变量一样简单。
¥And thanks to the power of Tailwind, tweaking the visual style of these components is as simple as editing the variables in your CSS file.
现在就这些了!你可以在 UI 文档页面 上看到我们目前提供的所有组件。如果你有任何关于新组件的建议或请求,欢迎随时告知我们。或者,也许你已经开始使用 shadcn 和 React Flow UI 构建一些东西了。无论哪种方式,请务必在我们的 Discord 服务器 或 Twitter 上告知我们!
¥That’s all for now! You can see all the components we currently have available over on the UI docs page. If you have any suggestions or requests for new components we’d love to hear about them. Or perhaps you’re already starting to build something with shadcn and React Flow UI. Either way make sure you let us know on our Discord server or on Twitter !