@antv/s2-react
Version:
use S2 with react
112 lines • 4.02 kB
JavaScript
import { __awaiter } from "tslib";
/**
* 兼容 React 16/17/18/19 的挂载和卸载
*/
import { version } from 'react';
import * as ReactDOM from 'react-dom';
export const S2_REACT_ROOT_SYMBOL_ID = `__s2_react_root__`;
// 1. 避免直接解构 render,防止 React 19 报错
// 仅保留类型定义和必要的静态属性引用
const ReactDOMClone = Object.assign({}, ReactDOM);
let createRootFn;
// 2. 异步获取 createRoot
// 这是一个懒加载单例,只在第一次渲染时执行
function getCreateRoot() {
return __awaiter(this, void 0, void 0, function* () {
if (createRootFn) {
return createRootFn;
}
// 优先尝试 React 18 的同步入口 (如果有)
if (ReactDOMClone.createRoot) {
createRootFn = ReactDOMClone.createRoot;
return createRootFn;
}
// React 19+ 或 React 18 Client 模式
try {
const client = yield import('react-dom/client');
createRootFn = client.createRoot;
return createRootFn;
}
catch (e) {
throw new Error('[S2] React 18+ detected but failed to load createRoot. Please ensure react-dom is installed correctly.', { cause: e });
}
});
}
export const isLegacyReactVersion = () => {
const mainVersion = Number((version || '').split('.')[0]);
return mainVersion < 18;
};
/**
* 由于兼容的关系, 没有从 "react-dom/client" 引入 createRoot, 会报 warning
* https://github.com/facebook/react/issues/24372
*/
function toggleWarning(skip) {
const { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } = ReactDOMClone;
if (__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED &&
typeof __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED === 'object') {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.usingClientEntryPoint =
skip;
}
}
// ========================== Render ==========================
function modernRender(node, container) {
return __awaiter(this, void 0, void 0, function* () {
toggleWarning(true);
let root = container[S2_REACT_ROOT_SYMBOL_ID];
if (!root) {
// 异步等待 createRoot 加载完成
const createRoot = yield getCreateRoot();
root = createRoot(container);
container[S2_REACT_ROOT_SYMBOL_ID] = root;
}
toggleWarning(false);
root.render(node);
return root;
});
}
function legacyRender(node, container) {
const reactRender = ReactDOMClone.render;
if (reactRender) {
reactRender(node, container);
}
else {
throw new Error('[S2] Failed to render. React 16/17 detected but ReactDOM.render is empty');
}
}
// 注意:这里变成了 async 函数
export function reactRender(node, container) {
return __awaiter(this, void 0, void 0, function* () {
if (!isLegacyReactVersion()) {
yield modernRender(node, container);
return;
}
legacyRender(node, container);
});
}
// ========================= Unmount ==========================
function modernUnmount(container) {
return Promise.resolve().then(() => {
var _a;
(_a = container === null || container === void 0 ? void 0 : container[S2_REACT_ROOT_SYMBOL_ID]) === null || _a === void 0 ? void 0 : _a.unmount();
container === null || container === void 0 ? true : delete container[S2_REACT_ROOT_SYMBOL_ID];
});
}
function legacyUnmount(container) {
const unmount = ReactDOMClone.unmountComponentAtNode;
if (container && unmount) {
unmount(container);
}
}
export function reactUnmount(container) {
if (!isLegacyReactVersion()) {
return modernUnmount(container);
}
return legacyUnmount(container);
}
export function forceClearContent(container) {
if (isLegacyReactVersion()) {
return legacyUnmount(container);
}
return modernRender(null, container);
}
//# sourceMappingURL=reactRender.js.map