Skip to content

Commit df5a0c9

Browse files
HaixingOoOHeisinguyarn
authored
feat(React19): add React19 adapter (Tencent#3640)
* feat(packages): add React19 adapter * chore(package): update package config * chore(react-render): adjust reactRender code * chore(react-render): optimize react-render code * chore(common-js): fix import error * chore: optimize the use of react19 adapter * chore: add react-19-adapter to sideEffects * feat: add adapter file compile * chore(react-render): adjust React19 use tdesign-react way * chore(common): sync packages/common --------- Co-authored-by: Heising <[email protected]> Co-authored-by: wū yāng <[email protected]>
1 parent 20d8aba commit df5a0c9

File tree

7 files changed

+114
-14
lines changed

7 files changed

+114
-14
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createRoot } from 'react-dom/client';
2+
import { renderAdapter } from './react-render';
3+
4+
renderAdapter(createRoot);

packages/components/_util/react-render.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// @ts-ignore
33
import type * as React from 'react';
44
import * as ReactDOM from 'react-dom';
5-
import { createRoot as createRootClient } from 'react-dom/client';
65
import type { Root } from 'react-dom/client';
76

87
// Let compiler not to search module usage
@@ -20,13 +19,16 @@ type CreateRoot = (container: ContainerType) => Root;
2019
// @ts-ignore
2120
const { version, render: reactRender, unmountComponentAtNode } = fullClone;
2221

23-
let createRoot: CreateRoot;
22+
let legacyCreateRoot: CreateRoot;
2423
try {
2524
const mainVersion = Number((version || '').split('.')[0]);
2625
if (mainVersion >= 18 && mainVersion < 19) {
27-
({ createRoot } = fullClone);
28-
} else if (mainVersion >= 19) {
29-
createRoot = createRootClient;
26+
legacyCreateRoot = fullClone.createRoot;
27+
}
28+
if (process.env.NODE_ENV !== 'production' && mainVersion >= 19) {
29+
console.warn(
30+
'TDesign warning: Please import react-19-adapter in React 19, See link: https://github.com/Tencent/tdesign-react/blob/develop/packages/tdesign-react/site/docs/getting-started.md#如何在-react-19-中使用',
31+
);
3032
}
3133
} catch (e) {
3234
// Do nothing;
@@ -52,7 +54,7 @@ type ContainerType = (Element | DocumentFragment) & {
5254

5355
function modernRender(node: React.ReactElement, container: ContainerType) {
5456
toggleWarning(true);
55-
const root = container[MARK] || createRoot(container);
57+
const root = container[MARK] || legacyCreateRoot(container);
5658
toggleWarning(false);
5759

5860
root.render(node);
@@ -66,7 +68,7 @@ function legacyRender(node: React.ReactElement, container: ContainerType) {
6668
}
6769

6870
export function render(node: React.ReactElement, container: ContainerType) {
69-
if (createRoot) {
71+
if (legacyCreateRoot) {
7072
modernRender(node, container);
7173
return;
7274
}
@@ -90,10 +92,21 @@ function legacyUnmount(container: ContainerType) {
9092
}
9193

9294
export async function unmount(container: ContainerType) {
93-
if (createRoot !== undefined) {
95+
if (legacyCreateRoot !== undefined) {
9496
// Delay to unmount to avoid React 18 sync warning
9597
return modernUnmount(container);
9698
}
9799

98100
legacyUnmount(container);
99101
}
102+
103+
/**
104+
* @deprecated Set React render function for compatible usage.
105+
* This is internal usage only compatible with React 19.
106+
* And will be removed in next major version.
107+
*/
108+
export function renderAdapter(render?: CreateRoot) {
109+
if (render) {
110+
legacyCreateRoot = render;
111+
}
112+
}

packages/tdesign-react/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
"dist/*",
2323
"site/*",
2424
"es/**/style/**",
25-
"esm/**/style/**"
25+
"esm/**/style/**",
26+
"es/_util/react-19-adapter.js",
27+
"esm/_util/react-19-adapter.js",
28+
"lib/_util/react-19-adapter.js"
2629
],
2730
"publishConfig": {
2831
"registry": "https://registry.npmjs.org/"

packages/tdesign-react/site/docs/getting-started.en-US.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,13 @@ module.exports = {
8888

8989
### How to use in React 19
9090

91-
If you need to use TDesign React in React 19, please install version `[email protected]`. Since it cannot simultaneously support React 16–19, we will soon provide a branch version for long-term compatibility with React 19.
91+
If you need to use it with React 19, please install at least `[email protected]`.
92+
93+
```js
94+
import React from "react";
95+
import { createRoot } from "react-dom/client";
96+
import "tdesign-react/es/_util/react-19-adapter";
97+
```
9298

9399
### How to use React with Next.js
94100

@@ -101,6 +107,8 @@ When using Next.js, you need to adjust how you use these components.
101107

102108
import { Button } from 'tdesign-react/lib/';
103109
import 'tdesign-react/dist/tdesign.css';
110+
// Use it in React19 and introduce the following line
111+
import "tdesign-react/lib/_util/react-19-adapter";
104112
```
105113

106114
In addition, the code exported by the `lib` package is written in `es6` and is located in the `node_modules`. It will be skipped by Webpack during compilation, and you need to configure it in `next.config.js`

packages/tdesign-react/site/docs/getting-started.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,13 @@ module.exports = {
9191
```
9292
### 如何在 React 19 中使用
9393

94-
如果您需要在 React 19 中使用,请安装 `[email protected]` 这个版本;因为无法同时兼容React 16 -19,我们接下来会专门提供一个分支版本,长期支持在 React 19 的使用 TDesign React。
94+
如果您需要在 React 19 中使用,请安装最低 `[email protected]` 这个版本。
95+
96+
```js
97+
import React from "react";
98+
import { createRoot } from "react-dom/client";
99+
import "tdesign-react/es/_util/react-19-adapter";
100+
```
95101

96102
### 如何在 Next.js 中使用
97103

@@ -104,6 +110,8 @@ module.exports = {
104110

105111
import { Button } from 'tdesign-react/lib/'; // 按需引入无样式组件代码
106112
import 'tdesign-react/dist/tdesign.css'; // 全局引入所有组件样式代码
113+
// 在 React19 使用,引入下面这行
114+
import "tdesign-react/lib/_util/react-19-adapter";
107115
```
108116

109117
此外 `lib` 包导出的是 `es6` 的代码且在 `node_modules` 中,会被 `webpack` 在编译时跳过,还需配置下 `next.config.js`

script/rollup.config.js

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import multiInput from 'rollup-plugin-multi-input';
1414
import nodeResolve from '@rollup/plugin-node-resolve';
1515
import staticImport from 'rollup-plugin-static-import';
1616
import ignoreImport from 'rollup-plugin-ignore-import';
17-
import { resolve } from 'path';
17+
import { resolve, join } from 'path';
18+
import { copy as fileCopy } from 'fs-extra';
1819

1920
import pkg from '../packages/tdesign-react/package.json';
2021

@@ -34,6 +35,7 @@ const inputList = [
3435
'packages/components/**/*.ts',
3536
'packages/components/**/*.jsx',
3637
'packages/components/**/*.tsx',
38+
'!packages/components/_util/react-19-adapter.ts', // 单独处理 external
3739
'!packages/components/**/_example',
3840
'!packages/components/**/_example-js',
3941
'!packages/components/**/*.d.ts',
@@ -274,4 +276,66 @@ const resetCss = {
274276
plugins: [postcss({ extract: true })],
275277
};
276278

277-
export default [cssConfig, libConfig, cjsConfig, esConfig, esmConfig, umdConfig, umdMinConfig, resetCss];
279+
// 适配 React 19 的 adapter
280+
const adapterInput = 'packages/components/_util/react-19-adapter.ts';
281+
const adapterExternal = ['react-dom', './react-render'];
282+
283+
function postWritePlugin() {
284+
return {
285+
name: 'post-write-plugin',
286+
async writeBundle(outputOptions) {
287+
// 1. 获取写入的目录路径
288+
const outputDir = outputOptions.dir;
289+
const filePath = join(outputDir, 'react-19-adapter.js');
290+
291+
async function copyMultipleDest(src, destArray) {
292+
try {
293+
// 并行复制到所有目标路径
294+
await Promise.all(destArray.map((dest) => fileCopy(src, dest)));
295+
console.log('adapter has been coped.');
296+
} catch (err) {
297+
console.error('copy failed:', err);
298+
}
299+
}
300+
301+
copyMultipleDest(filePath, [
302+
'packages/tdesign-react/lib/_util/react-19-adapter.js',
303+
'packages/tdesign-react/esm/_util/react-19-adapter.js',
304+
]);
305+
},
306+
};
307+
}
308+
309+
const adapterEsConfig = {
310+
input: adapterInput,
311+
external: adapterExternal,
312+
plugins: [postWritePlugin()],
313+
output: {
314+
banner,
315+
dir: 'packages/tdesign-react/es/_util/',
316+
format: 'esm',
317+
},
318+
};
319+
320+
const adapterCjsConfig = {
321+
input: adapterInput,
322+
external: adapterExternal,
323+
output: {
324+
banner,
325+
dir: 'packages/tdesign-react/cjs/_util/',
326+
format: 'cjs',
327+
},
328+
};
329+
330+
export default [
331+
cssConfig,
332+
libConfig,
333+
cjsConfig,
334+
esConfig,
335+
esmConfig,
336+
umdConfig,
337+
umdMinConfig,
338+
resetCss,
339+
adapterEsConfig,
340+
adapterCjsConfig,
341+
];

0 commit comments

Comments
 (0)