跳到主要内容

React 节点

在本章节中主要介绍 React 节点相关的知识,通过阅读,你可以了解到

  • 如何使用 React 组件来渲染节点内容
  • 如何更新节点内容

渲染节点

我们提供了一个独立的包 @antv/x6-react-shape,用于通过 React 渲染节点。

注意

版本兼容关系:X6 3.x 须使用 x6-react-shape 3.x 版本。同时 x6-react-shape 自 2.0.8 起仅支持 React 18 及以上;若项目低于 React 18,请将 x6-react-shape 锁定到 2.0.8 并使用 X6 2.x。

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,
})
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'
import { register } from '@antv/x6-react-shape'
import { Progress } from 'antd'

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,
})

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    graph.addNode({
      shape: 'custom-basic-react-node',
      x: 60,
      y: 100,
    })

    graph.centerContent()
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="react-basic-app">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

更新节点

与 HTML 类似,注册节点时可提供 effect 字段(当前节点的 props 数组)。当 effect 中任一 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)
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph, Node } from '@antv/x6'
import { register } from '@antv/x6-react-shape'
import { Progress } from 'antd'

const NodeComponent = ({ node }: { node: Node }) => {
  const { progress } = node.getData()
  return (
    <div className="react-node">
      <Progress type="circle" percent={progress} width={80} />
    </div>
  )
}

register({
  shape: 'custom-update-react-node',
  width: 100,
  height: 100,
  effect: ['data'],
  component: NodeComponent,
})

export default class Example extends React.Component {
  private container: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    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)

    graph.centerContent()
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  render() {
    return (
      <div className="react-update-app ">
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

Portal 方式

上述渲染方式有一个缺点:组件会被直接渲染到节点的 DOM 中,方式如下。

import { createRoot, Root } from 'react-dom/client'

const root = createRoot(container) // container 为节点容器
root.render(component)

此时组件不在常规渲染树中,因而无法访问外部 Context。若有此需求,请使用 Portal 模式来渲染 React 组件。

import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'
import { register, getProvider } from '@antv/x6-react-shape'
import { Progress, Button } from 'antd'

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,
})

export default class Example extends React.Component {
  private container: HTMLDivElement

  state = {
    progress: 30,
  }

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })

    graph.addNode({
      shape: 'custom-portal-react-node',
      x: 60,
      y: 100,
    })

    graph.centerContent()
  }

  changeProgress = () => {
    this.setState({
      progress: (this.state.progress + 10) % 100,
    })
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  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>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
© Copyright 2026 Ant Group Co., Ltd..备案号:京ICP备15032932号-38
全站搜索
文档与示例输入关键词开始搜索

输入关键词,查找文档、API 与示例

↑↓选择↵打开Esc关闭