mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 02:49:18 +08:00
chore: remove unstable api for React 19 compitable (#55274)
* chore: remove unstable api for React 19 compitable * update * update * update * update antd-token-previewer * update * update --------- Co-authored-by: 遇见同学 <1875694521@qq.com>
This commit is contained in:
@@ -211,7 +211,6 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
'react-dom': '^19.0.0',
|
||||
'@types/react': '^19.0.0',
|
||||
'@types/react-dom': '^19.0.0',
|
||||
'@ant-design/v5-patch-for-react-19': '^1.0.3',
|
||||
},
|
||||
demoJsContent,
|
||||
indexCssContent,
|
||||
|
||||
@@ -122,7 +122,6 @@ const getStackblitzConfig = ({
|
||||
// main.tsx
|
||||
[`src/main.${_suffix}`]: `import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
import Demo from './demo';
|
||||
|
||||
createRoot(document.getElementById('container')${suffix === 'tsx' ? '!' : ''}).render(
|
||||
|
||||
@@ -22,8 +22,6 @@ import type { SimpleComponentClassNames, SiteContextProps } from '../slots/SiteC
|
||||
import SiteContext from '../slots/SiteContext';
|
||||
import { isLocalStorageNameSupported } from '../utils';
|
||||
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
|
||||
type SiteState = Partial<Omit<SiteContextProps, 'updateSiteContext'>>;
|
||||
|
||||
const RESPONSIVE_MOBILE = 768;
|
||||
|
||||
@@ -74,7 +74,6 @@ exports[`antd exports modules correctly 1`] = `
|
||||
"message",
|
||||
"notification",
|
||||
"theme",
|
||||
"unstableSetRender",
|
||||
"version",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Modal, unstableSetRender } from 'antd';
|
||||
|
||||
import { waitFakeTimer19 } from '../../tests/utils';
|
||||
|
||||
// TODO: Remove this. Mock for React 19
|
||||
jest.mock('react-dom', () => {
|
||||
const realReactDOM = jest.requireActual('react-dom');
|
||||
|
||||
if (realReactDOM.version.startsWith('19')) {
|
||||
const realReactDOMClient = jest.requireActual('react-dom/client');
|
||||
realReactDOM.createRoot = realReactDOMClient.createRoot;
|
||||
}
|
||||
|
||||
return realReactDOM;
|
||||
});
|
||||
|
||||
describe('unstable', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('unstableSetRender', async () => {
|
||||
if (ReactDOM.version.startsWith('19')) {
|
||||
unstableSetRender((node, container) => {
|
||||
const root = (ReactDOM as any).createRoot(container);
|
||||
root.render(node);
|
||||
return async () => {
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
|
||||
Modal.info({ content: 'unstableSetRender' });
|
||||
|
||||
await waitFakeTimer19();
|
||||
|
||||
expect(document.querySelector('.ant-modal')).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it('unstableSetRender without param', async () => {
|
||||
const currentRender = unstableSetRender();
|
||||
expect(currentRender).toBeInstanceOf(Function);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import CSSMotion from '@rc-component/motion';
|
||||
import raf from '@rc-component/util/lib/raf';
|
||||
import { render, unmount } from '@rc-component/util/lib/React/render';
|
||||
import { composeRef } from '@rc-component/util/lib/ref';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
import type { WaveProps } from '.';
|
||||
import { unstableSetRender } from '../../config-provider/UnstableContext';
|
||||
import type { UnmountType } from '../../config-provider/UnstableContext';
|
||||
import { TARGET_CLS } from './interface';
|
||||
import type { ShowWaveEffect } from './interface';
|
||||
import { getTargetWaveColor } from './util';
|
||||
@@ -19,21 +18,13 @@ export interface WaveEffectProps {
|
||||
className: string;
|
||||
target: HTMLElement;
|
||||
component?: string;
|
||||
registerUnmount: () => UnmountType | null;
|
||||
colorSource?: WaveProps['colorSource'];
|
||||
}
|
||||
|
||||
const WaveEffect = (props: WaveEffectProps) => {
|
||||
const { className, target, component, registerUnmount, colorSource } = props;
|
||||
const { className, target, component, colorSource } = props;
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
// ====================== Refs ======================
|
||||
const unmountRef = React.useRef<UnmountType>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
unmountRef.current = registerUnmount();
|
||||
}, []);
|
||||
|
||||
// ===================== Effect =====================
|
||||
const [color, setWaveColor] = React.useState<string | null>(null);
|
||||
const [borderRadius, setBorderRadius] = React.useState<number[]>([]);
|
||||
@@ -129,7 +120,7 @@ const WaveEffect = (props: WaveEffectProps) => {
|
||||
onAppearEnd={(_, event) => {
|
||||
if (event.deadline || (event as TransitionEvent).propertyName === 'opacity') {
|
||||
const holder = divRef.current?.parentElement!;
|
||||
unmountRef.current?.().then(() => {
|
||||
unmount(holder).then(() => {
|
||||
holder?.remove();
|
||||
});
|
||||
}
|
||||
@@ -162,18 +153,7 @@ const showWaveEffect: ShowWaveEffect = (target, info) => {
|
||||
holder.style.top = '0px';
|
||||
target?.insertBefore(holder, target?.firstChild);
|
||||
|
||||
const reactRender = unstableSetRender();
|
||||
|
||||
let unmountCallback: UnmountType | null = null;
|
||||
|
||||
function registerUnmount() {
|
||||
return unmountCallback;
|
||||
}
|
||||
|
||||
unmountCallback = reactRender(
|
||||
<WaveEffect {...info} target={target} registerUnmount={registerUnmount} />,
|
||||
holder,
|
||||
);
|
||||
render(<WaveEffect {...info} target={target} />, holder);
|
||||
};
|
||||
|
||||
export default showWaveEffect;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import type * as React from 'react';
|
||||
import { render, unmount } from '@rc-component/util/lib/React/render';
|
||||
|
||||
export type UnmountType = () => Promise<void>;
|
||||
export type RenderType = (
|
||||
node: React.ReactElement,
|
||||
container: Element | DocumentFragment,
|
||||
) => UnmountType;
|
||||
|
||||
const defaultReactRender: RenderType = (node, container) => {
|
||||
render(node, container);
|
||||
return () => {
|
||||
return unmount(container);
|
||||
};
|
||||
};
|
||||
|
||||
let unstableRender: RenderType = defaultReactRender;
|
||||
|
||||
/**
|
||||
* @deprecated Set React render function for compatible usage.
|
||||
* This is internal usage only compatible with React 19.
|
||||
* And will be removed in next major version.
|
||||
*/
|
||||
export function unstableSetRender(render?: RenderType) {
|
||||
if (render) {
|
||||
unstableRender = render;
|
||||
}
|
||||
return unstableRender;
|
||||
}
|
||||
@@ -175,7 +175,3 @@ export type { DraggerProps, UploadFile, UploadProps } from './upload';
|
||||
export { default as version } from './version';
|
||||
export { default as Watermark } from './watermark';
|
||||
export type { WatermarkProps } from './watermark';
|
||||
|
||||
// TODO: Remove in v6
|
||||
/* eslint-disable-next-line perfectionist/sort-exports */
|
||||
export { unstableSetRender } from './config-provider/UnstableContext';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { render } from '@rc-component/util/lib/React/render';
|
||||
|
||||
import { AppConfigContext } from '../app/context';
|
||||
import ConfigProvider, { ConfigContext, globalConfig, warnContext } from '../config-provider';
|
||||
import { unstableSetRender } from '../config-provider/UnstableContext';
|
||||
import type {
|
||||
ArgsProps,
|
||||
ConfigOptions,
|
||||
@@ -132,13 +132,10 @@ const flushMessageQueue = () => {
|
||||
|
||||
// Delay render to avoid sync issue
|
||||
act(() => {
|
||||
const reactRender = unstableSetRender();
|
||||
|
||||
reactRender(
|
||||
render(
|
||||
<GlobalHolderWrapper
|
||||
ref={(node) => {
|
||||
const { instance, sync } = node || {};
|
||||
|
||||
// React 18 test env will throw if call immediately in ref
|
||||
Promise.resolve().then(() => {
|
||||
if (!newMessage.instance && instance) {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { render, unmount } from '@rc-component/util/lib/React/render';
|
||||
|
||||
import warning from '../_util/warning';
|
||||
import ConfigProvider, { ConfigContext, globalConfig, warnContext } from '../config-provider';
|
||||
import { unstableSetRender } from '../config-provider/UnstableContext';
|
||||
import type { UnmountType } from '../config-provider/UnstableContext';
|
||||
import type { ConfirmDialogProps } from './ConfirmDialog';
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
import destroyFns from './destroyFns';
|
||||
@@ -72,8 +71,6 @@ export default function confirm(config: ModalFuncProps) {
|
||||
let currentConfig = { ...config, close, open: true } as any;
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
|
||||
let reactUnmount: UnmountType;
|
||||
|
||||
function destroy(...args: any[]) {
|
||||
const triggerCancel = args.some((param) => param?.triggerCancel);
|
||||
if (triggerCancel) {
|
||||
@@ -87,10 +84,12 @@ export default function confirm(config: ModalFuncProps) {
|
||||
}
|
||||
}
|
||||
|
||||
reactUnmount();
|
||||
unmount(container).then(() => {
|
||||
// Do nothing
|
||||
});
|
||||
}
|
||||
|
||||
function render(props: any) {
|
||||
function scheduleRender(props: any) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
/**
|
||||
@@ -105,11 +104,9 @@ export default function confirm(config: ModalFuncProps) {
|
||||
|
||||
const dom = <ConfirmDialogWrapper {...props} />;
|
||||
|
||||
const reactRender = unstableSetRender();
|
||||
|
||||
reactUnmount = reactRender(
|
||||
render(
|
||||
<ConfigProvider prefixCls={rootPrefixCls} iconPrefixCls={iconPrefixCls} theme={theme}>
|
||||
{global.holderRender ? global.holderRender(dom) : dom}
|
||||
{typeof global.holderRender === 'function' ? global.holderRender(dom) : dom}
|
||||
</ConfigProvider>,
|
||||
container,
|
||||
);
|
||||
@@ -129,7 +126,7 @@ export default function confirm(config: ModalFuncProps) {
|
||||
},
|
||||
};
|
||||
|
||||
render(currentConfig);
|
||||
scheduleRender(currentConfig);
|
||||
}
|
||||
|
||||
function update(configUpdate: ConfigUpdate) {
|
||||
@@ -141,10 +138,10 @@ export default function confirm(config: ModalFuncProps) {
|
||||
...configUpdate,
|
||||
};
|
||||
}
|
||||
render(currentConfig);
|
||||
scheduleRender(currentConfig);
|
||||
}
|
||||
|
||||
render(currentConfig);
|
||||
scheduleRender(currentConfig);
|
||||
|
||||
destroyFns.push(close);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { render } from '@rc-component/util/lib/React/render';
|
||||
|
||||
import { AppConfigContext } from '../app/context';
|
||||
import ConfigProvider, { ConfigContext, globalConfig, warnContext } from '../config-provider';
|
||||
import { unstableSetRender } from '../config-provider/UnstableContext';
|
||||
import type { ArgsProps, GlobalConfigProps, NotificationInstance } from './interface';
|
||||
import PurePanel from './PurePanel';
|
||||
import useNotification, { useInternalNotification } from './useNotification';
|
||||
@@ -126,13 +126,10 @@ const flushNotificationQueue = () => {
|
||||
|
||||
// Delay render to avoid sync issue
|
||||
act(() => {
|
||||
const reactRender = unstableSetRender();
|
||||
|
||||
reactRender(
|
||||
render(
|
||||
<GlobalHolderWrapper
|
||||
ref={(node) => {
|
||||
const { instance, sync } = node || {};
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
if (!newNotification.instance && instance) {
|
||||
newNotification.instance = instance;
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
order: 1
|
||||
title: React 19 Compatibility
|
||||
tag: New
|
||||
---
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
:::info{title="Compatibility Interface"}
|
||||
antd v5 is compatible with React 16 ~ 18 by default, and most features are also compatible with React 19. A few issues are listed below, and the following compatibility methods can be used for adaptation. This method and interface will be removed in v6.
|
||||
::::
|
||||
|
||||
### React 19 Compatibility Issues
|
||||
|
||||
Due to React 19 adjusting the export method of `react-dom`, antd cannot directly use the `ReactDOM.render` method. Therefore, using antd will encounter the following issues:
|
||||
|
||||
- Wave effect does not work properly
|
||||
- `Modal`、`Notification`、`Message` and other components' static methods are invalid (hooks invocation methods are not affected).
|
||||
|
||||
Therefore, you need to use a compatibility configuration to make antd work properly in React 19.
|
||||
|
||||
### Compatibility Methods
|
||||
|
||||
You can choose one of the following methods, and it is recommended to use the compatibility package first.
|
||||
|
||||
#### Compatibility Package
|
||||
|
||||
Install the compatibility package
|
||||
|
||||
<InstallDependencies npm='npm install @ant-design/v5-patch-for-react-19 --save' yarn='yarn add @ant-design/v5-patch-for-react-19' pnpm='pnpm add @ant-design/v5-patch-for-react-19 --save' bun='bun add @ant-design/v5-patch-for-react-19'></InstallDependencies>
|
||||
|
||||
Import the compatibility package at the application entry
|
||||
|
||||
```tsx
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
```
|
||||
|
||||
#### unstableSetRender
|
||||
|
||||
Once again, please use the compatibility package first. Only for special scenarios such as umd, micro-applications, etc., use the `unstableSetRender` method. `unstableSetRender` is a low-level registration method that allows developers to modify the rendering method of ReactDOM. Write the following code at the entry of your application:
|
||||
|
||||
```js
|
||||
import { unstableSetRender } from 'antd';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
unstableSetRender((node, container) => {
|
||||
container._reactRoot ||= createRoot(container);
|
||||
const root = container._reactRoot;
|
||||
root.render(node);
|
||||
return async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
```
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
order: 1
|
||||
title: React 19 兼容
|
||||
tag: New
|
||||
---
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
:::info{title="兼容接口"}
|
||||
antd v5 默认兼容 React 16 ~ 18 版本,绝大多数功能也兼容于 React 19 版本。少数问题如下所示,可使用以下兼容方法进行适配。该方式以及接口将在 v6 被移除。
|
||||
:::
|
||||
|
||||
### React 19 兼容问题
|
||||
|
||||
由于 React 19 调整了 `react-dom` 的导出方式,导致 antd 无法直接使用 `ReactDOM.render` 方法。因而使用 antd 会遇到以下问题:
|
||||
|
||||
- 波纹特效无法正常工作
|
||||
- `Modal`、`Notification`、`Message` 等组件的静态方法无效(hooks 调用方式不受影响)
|
||||
|
||||
因而需要通过兼容配置,使 antd 在 React 19 中正常工作。
|
||||
|
||||
### 兼容方式
|
||||
|
||||
以下方法任选其一,其中优先推荐使用兼容包。
|
||||
|
||||
#### 兼容包
|
||||
|
||||
安装兼容包
|
||||
|
||||
<InstallDependencies npm='npm install @ant-design/v5-patch-for-react-19 --save' yarn='yarn add @ant-design/v5-patch-for-react-19' pnpm='pnpm add @ant-design/v5-patch-for-react-19 --save' bun='bun add @ant-design/v5-patch-for-react-19'></InstallDependencies>
|
||||
|
||||
在应用入口处引入兼容包
|
||||
|
||||
```tsx
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
```
|
||||
|
||||
#### unstableSetRender
|
||||
|
||||
再次提醒,默认情况下,请优先使用兼容包。除非对于 umd、微应用等特殊场景,才使用 `unstableSetRender` 方法。`unstableSetRender` 为底层注册方法,允许开发者修改 ReactDOM 的渲染方法。在你的应用入口处写入:
|
||||
|
||||
```js
|
||||
import { unstableSetRender } from 'antd';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
unstableSetRender((node, container) => {
|
||||
container._reactRoot ||= createRoot(container);
|
||||
const root = container._reactRoot;
|
||||
root.render(node);
|
||||
return async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
```
|
||||
@@ -161,7 +161,6 @@
|
||||
"@ant-design/compatible": "^5.1.4",
|
||||
"@ant-design/happy-work-theme": "^1.0.1",
|
||||
"@ant-design/tools": "^19.0.3",
|
||||
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
||||
"@ant-design/x": "^1.6.1",
|
||||
"@antfu/eslint-config": "^5.4.1",
|
||||
"@antv/g6": "^4.8.25",
|
||||
@@ -225,7 +224,7 @@
|
||||
"ali-oss": "^6.23.0",
|
||||
"antd-img-crop": "^4.27.0",
|
||||
"antd-style": "4.0.0-alpha.1",
|
||||
"antd-token-previewer": "3.0.0-alpha.1",
|
||||
"antd-token-previewer": "3.0.0-alpha.3",
|
||||
"axios": "^1.12.2",
|
||||
"chalk": "^5.6.2",
|
||||
"cheerio": "^1.1.2",
|
||||
|
||||
@@ -74,7 +74,6 @@ exports[`antd dist files exports modules correctly 1`] = `
|
||||
"message",
|
||||
"notification",
|
||||
"theme",
|
||||
"unstableSetRender",
|
||||
"version",
|
||||
]
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user