目前 Portal.getProvider 返回的 X6ReactPortalProvider 只支持单实例画布,同一个页面存在多个antv x6画布实例时会导致 react 组件节点内useContext 值有问题
- Dominant language
- TypeScript
- Stars
- 6.7k
- Forks
- 1.9k
- PR merge metrics
- No merged PRs in 30d
Description
### 功能描述
# 背景:
一个页面中会存在多个 antv x6 的画布实例, 比如说,会依次初始化 A、B、C 三个画布然后共存。伪代码示意图如下
```
```
会发现一个神奇的现象:第一个画布 AX6Graph 中,新建react 组件节点通过 useContext 获取的 context 的值是 第三个 CX6Graph 画布的CContext。
# 问题分析:
问题出在 Portal.getProvider 方法上, 这个方法是为了让每一个 React 节点的组件都能获取到全局的上下文
在默认情况下,每一个 React 节点的组件都会作为一个独立的 React 文档进行渲染(即使用 [ReactDOM.createRoot](https://reactjs.org/docs/react-dom-client.html#createroot)),此时 React 组件将不能正常获得主应用的上下文(Context),所以得用 antv 额外提供的 Portal 机制,用法如下:

看源码定义如下, Portal 是一个ts语法的namespace ,ts 的 namespace 会被编译成 js 的一个匿名自执行函数,该自执行函数只会在一个浏览器页面中执行一次,

如下所示: dispatch 变量成为了闭包,在多次调用 Portal.getProvider 方法后,永远指向最后一次调用生成的 mutate。

dispatch 方法会被 Portal.connect 所调用, Portal.connect 该方法会在画布的每一个节点渲染时使用。
当 A 画布新加一个节点需要渲染时会执行 Portal.connect,然后调用 dispatch 去更新内部状态为了让画布中渲染出最新的节点,这步没问题。
问题就出在,这里调用的 dispatch 方法已经被覆盖了!!永远指向最后一次生成的画布的上下文!从而导致 A 画布新加的节点 useContex 获取到的东西是 C 画布的。
### 期望解决方案
别用 namespace 了,ts 都不推荐了
```
TypeScript 放弃了自身的模块系统,转而使用 ES 规范中的模块系统,相应的 Tracker 有:
- [Support ES6 import and export declarations by ahejlsberg · Pull Request #1983 · Microsoft/TypeScript](https://link.zhihu.com/?target=https://github.com/Microsoft/TypeScript/pull/1983)
- [Complete support for ES6 modules by ahejlsberg · Pull Request #2197 · Microsoft/TypeScript](https://link.zhihu.com/?target=https://github.com/Microsoft/TypeScript/pull/2197)
- [Revised ES6 modules by ahejlsberg · Pull Request #2460 · Microsoft/TypeScript](https://link.zhihu.com/?target=https://github.com/Microsoft/TypeScript/pull/2460)
- [ES6 Modules · Issue #2242 · Microsoft/TypeScript](https://link.zhihu.com/?target=https://github.com/Microsoft/TypeScript/issues/2242)
第三个 PR 中,详细阐述了原 TypeScript External Module 与新 ES Module 的对应关系(然而有些方面并不能很好地对应起来)。最后一个 Issue 的后续讨论中也有很多关于原 Internal Module 与新 ES Module 的相关内容。
当然原有的 External Module 语法依然能够继续使用,只是文档中几乎不再提及。
随着 ES Module 成为 TypeScript 的标准模块系统,原有的 Internal Module 和 External Module 概念容易产生不必要的误解,因此 Internal Module 改名为 Namespace,而 Module 就专门指代 ES Module。相应的 Tracker 在:
- [Namespace keyword · Issue #2159 · Microsoft/TypeScript](https://link.zhihu.com/?target=https://github.com/Microsoft/TypeScript/issues/2159)
- [Namespaces by ahejlsberg · Pull Request #2923 · Microsoft/TypeScript](https://link.zhihu.com/?target=https://github.com/Microsoft/TypeScript/pull/2923)
TypeScript 是一个对兼容性及其重视的项目,所以仍然保留了这些旧方案的可用性,但是在实际项目中应当尽可能避免使用。
````
最主要的原因是 ,namespace 编译出来是一个自执行函数,是单例的。
用 class 搞成支持多实例的吧
Contributor guide
Research direction
Start by reading the TypeScript Portal.getProvider and Portal.connect entry points, along with the ReactDOM.createRoot integration described in the issue. Trace how each canvas stores and updates its provider, then verify that multiple coexisting X6 canvases retain their own React context instead of sharing the last one created.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100