X6 React 节点渲染实战:使用 @antv/x6-react-shape 构建、更新与桥接 React 组件
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
本文以 X6 官方教程《React Nodes》为核心,系统讲解如何通过独立渲染包@antv/x6-react-shape在 X6 图中使用 React 组件渲染节点、通过effect字段实现节点内容的高效更新,以及借助Portal模式让节点组件访问应用级Context。读完本文,你将能够在自己的 X6 项目中直接落地"React 节点 + 数据驱动更新 + 外部状态桥接"的完整方案。
说明:本文对应的官方英文教程原文位于 react.en.md,文中的可运行示例代码可在仓库的 site/src/tutorial/intermediate/react 目录下找到完整源码。
一、为什么需要 React 节点
X6 本身使用 SVG 与 HTML 渲染节点内容(见项目描述与 入门教程 中 "X6 supports usingSVGandHTMLto render node content" 的说明)。在此基础上,社区和官方还提供了@antv/x6-react-shape这一独立渲染包,允许直接使用React 组件来描述节点内容。
使用 React 渲染节点的典型收益在官方快速入门文档中有明确场景:例如给节点添加右键菜单,用纯 SVG 实现会比较复杂,而用 React 节点配合成熟的组件库(如 Ant Design 的Dropdown)可以轻松完成。仓库中的 react-shape 入门示例 正是这一场景的完整实现:它注册了一个名为custom-react-node的 React 节点,节点组件内直接使用Dropdown组件并绑定contextMenu触发事件,再通过node.prop('label')读取节点 label 渲染文本。
二、安装与版本兼容性
@antv/x6-react-shape是一个独立 npm 包,需要与@antv/x6一起安装。当前仓库的站点工程中将其声明为:
"@antv/x6-react-shape": "^3.x"(见 site/package.json),对应 X6 3.x 主版本。
官方文档给出了必须注意的版本兼容约束:
- X6 3.x 必须搭配 x6-react-shape 3.x 使用;
- x6-react-shape 自2.0.8起仅支持React 18 及以上;
- 若你的项目使用 React 低于 18,请将 x6-react-shape 锁定到2.0.8,并搭配 X62.x使用。
因此在引入之前,请先核对你的 React 版本与 X6 版本,避免出现运行时兼容问题。
三、渲染节点:使用 register 注册 React 组件
渲染 React 节点的核心 API 是@antv/x6-react-shape导出的register函数。基本流程分三步:定义 React 组件、注册 shape、将 shape 添加到图中。
官方教程给出了最小示例:
import { register } from '@antv/x6-react-shape' const NodeComponent = () => { return ( <div className="react-node"> <Progress type="circle" percent={30} width={80} /> </div> ) } register({ shape: 'custom-basic-react-node', width: 100, height: 100, component: NodeComponent, }) graph.addNode({ shape: 'custom-basic-react-node', x: 60, y: 100, })其中register配置项的含义为:
shape:注册的节点形状名称,后续addNode/fromJSON时通过该名称引用;width/height:节点默认尺寸;component:用于渲染节点内容的 React 组件。
示例完整代码位于 basic/index.tsx,它演示了更贴近真实项目的用法:
- 组件内使用 Ant Design 的
Progress圆形进度条作为节点内容; - 注册后创建
Graph实例,设置画布背景色#F2F7FA; - 通过
graph.addNode({ shape: 'custom-basic-react-node', x: 60, y: 100 })添加节点; - 最后调用
graph.centerContent()将内容居中。
可以看到,React 组件被直接当作节点的"视觉外壳":节点的位置(x/y)仍由 X6 图模型管理,而外观完全由 React 组件自由绘制,这意味着你可以把任何 React 生态的组件(进度条、表格、下拉菜单、图表等)嵌入图中。
四、更新节点:effect 字段与数据驱动重渲染
静态渲染往往不够,业务中经常需要根据数据变化刷新节点内容。官方文档给出的机制与 X6 的 HTML 节点一脉相承(HTML 节点文档同样介绍了effect机制,见 html.en.md):注册节点时提供effect字段,它是节点props的数组;当其中任一 prop 发生变化时,对应的 React 组件会重新渲染。
register({ shape: 'custom-update-react-node', width: 100, height: 100, effect: ['data'], component: NodeComponent, }) const node = graph.addNode({ shape: 'custom-update-react-node', x: 60, y: 100, data: { progress: 30, }, }) setInterval(() => { const { progress } = node.getData<{ progress: number }>() node.setData({ progress: (progress + 10) % 100, }) }, 1000)这里的关键链路是:
effect: ['data']声明"当dataprop 变化时触发重渲染";addNode时传入初始data(progress: 30);- 定时器中通过
node.getData()读取当前数据、node.setData()写入新数据,从而驱动 React 组件重新渲染。
完整示例见 update/index.tsx,其节点组件接收node参数并读取数据:
const NodeComponent = ({ node }: { node: Node }) => { const { progress } = node.getData() return ( <div className="react-node"> <Progress type="circle" percent={progress} width={80} /> </div> ) }从源码结构可以看出,节点组件会收到 X6 的Node实例作为 props,因此组件内部既可以读data,也可以通过node.prop(...)读取其他属性(如 react-shape 入门示例 中用node.prop('label')读取 label)。这一模式在仓库的其他示例中也被反复使用,例如 examples/src/pages/react/index.tsx 中注册了algo-node-1节点,effect: ['data'],组件通过node.getData()读取name字段渲染算法节点名称,随后每秒node.setData({ name: ... })更新名称,展示数据驱动刷新的效果。
需要提醒的是:effect数组应只声明真正需要触发重渲染的 prop 键,避免无关数据变化引起不必要的组件刷新,从而影响大图渲染性能。
五、Portal 模式:让节点组件访问应用级 Context
默认情况下,@antv/x6-react-shape会把组件直接渲染到节点自身的 DOM 容器中,其内部实现等价于:
import { createRoot, Root } from 'react-dom/client' const root = createRoot(container) // container 为节点容器 root.render(component)这种渲染方式有一个显著缺点:组件不在常规的 React 组件树中,因此无法访问应用外层的Context(例如主题、国际化、数据仓库等)。当节点组件确实需要消费这些外部状态时,官方推荐使用Portal 模式。
Portal 模式的用法是调用getProvider()获取一个X6ReactPortalProvider组件,并将其挂载在提供Context的 Provider 之内:
import { register, getProvider } from '@antv/x6-react-shape' const X6ReactPortalProvider = getProvider() // 注意:一个 graph 只能申明一个 portal provider const ProgressContext = React.createContext(30) const NodeComponent = () => { const progress = React.useContext(ProgressContext) return ( <div className="react-node"> <Progress type="circle" percent={progress} width={80} /> </div> ) } register({ shape: 'custom-portal-react-node', width: 100, height: 100, component: NodeComponent, })渲染时把 Provider 与节点容器放在同一棵 React 树中:
render() { return ( <div className="react-portal-app"> <ProgressContext.Provider value={this.state.progress}> <X6ReactPortalProvider /> </ProgressContext.Provider> <div className="app-btns"> <Button onClick={this.changeProgress}>Add</Button> </div> <div className="app-content" ref={this.refContainer} /> </div> ) }完整示例见 portal/index.tsx。它的运行逻辑是:ProgressContext的 value 由 React 组件状态驱动,点击 "Add" 按钮通过setState改变进度值,由于节点组件是通过 Portal 挂在 Provider 之下的,useContext能实时拿到最新值并重渲染,无需走 X6 的setData链路。仓库的 examples/src/pages/react/portal.tsx 还提供了一个主题切换(light/dark)的 Portal 示例,演示节点组件通过useContext消费ThemeContext切换样式。
使用 Portal 模式时请注意官方注释中的约束:一个 graph 只能申明一个 portal provider,多个图需要分别管理各自的 Provider 实例。
六、两种更新路径的选择建议
结合官方文档与仓库示例,React 节点的数据流动有两条路径,可按场景选用:
| 更新方式 | 触发机制 | 适用场景 |
|---|---|---|
effect+node.setData() | X6 数据模型驱动,prop 变化触发组件重渲染 | 节点数据与图数据绑定,需要随图模型持久化、序列化(toJSON)的业务数据 |
Portal + 外部Context | React 自身状态驱动,setState/useState触发重渲染 | 需要消费应用级 Context(主题、国际化、全局 store),或状态由 React 树外部统一管理 |
当节点数据需要参与graph.toJSON()导出(如 入门教程 中介绍的序列化场景)时,优先选择effect+data方案,让业务数据保留在 X6 节点模型中;当节点仅需反映 React 树内部状态时,Portal 方案更加自然。
七、小结
本文围绕 X6 官方《React Nodes》教程,完整介绍了使用@antv/x6-react-shape的三个核心能力:
- 渲染:通过
register({ shape, width, height, component })将 React 组件注册为节点形状,随后即可在addNode/fromJSON中按 shape 名使用; - 更新:利用
effect字段声明需要监听的 prop(典型为data),配合node.getData()/node.setData()实现数据驱动的组件重渲染; - 桥接:使用
getProvider()获取 Portal Provider,并将其置于应用 Context 之下,让节点组件能够像普通 React 组件一样消费外部上下文。
同时务必牢记版本约束:X6 3.x 搭配 x6-react-shape 3.x,x6-react-shape 2.0.8 起要求 React 18+,旧项目需锁定 2.0.8 并配合 X6 2.x。掌握这三项能力后,你就可以在 X6 图中自由组合任意 React 生态组件,构建出交互复杂、数据实时刷新的可视化应用。
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考