refactor(组件): 将 destroyOnClose 替换为 destroyOnHidden 以优化组件销毁逻辑
refactor(React工具): 重构 React 18 兼容性补丁,合并开发环境修复功能 优化多个组件中的销毁逻辑,统一使用 destroyOnHidden 替代 destroyOnClose。同时重构 React 18 兼容性补丁代码,将开发环境的相关修复功能整合到 setupReactDevFixes 方法中,提高代码可维护性。
This commit is contained in:
@ -192,7 +192,7 @@ export const FlowTools = () => {
|
|||||||
onCancel={() => setBaseOpen(false)}
|
onCancel={() => setBaseOpen(false)}
|
||||||
okText={I18n.t('Save')}
|
okText={I18n.t('Save')}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
destroyOnClose
|
destroyOnHidden
|
||||||
>
|
>
|
||||||
<AForm form={baseForm} layout="vertical" preserve={false}>
|
<AForm form={baseForm} layout="vertical" preserve={false}>
|
||||||
<AForm.Item name="name" label={I18n.t('Flow Name')} rules={[{ required: true, message: I18n.t('Please input flow name') }, { max: 50, message: I18n.t('Max 50 characters') }]}>
|
<AForm.Item name="name" label={I18n.t('Flow Name')} rules={[{ required: true, message: I18n.t('Please input flow name') }, { max: 50, message: I18n.t('Max 50 characters') }]}>
|
||||||
|
|||||||
@ -6,40 +6,11 @@ import App from './App'
|
|||||||
import 'antd/dist/reset.css'
|
import 'antd/dist/reset.css'
|
||||||
import '@douyinfe/semi-ui/dist/css/semi.min.css'
|
import '@douyinfe/semi-ui/dist/css/semi.min.css'
|
||||||
import './styles/global.css'
|
import './styles/global.css'
|
||||||
// 导入 React 18 兼容性补丁
|
import { setupReactDevFixes } from './utils/react18-polyfill'
|
||||||
import { setupReact18Polyfill, setupDevSanitizeDOMProps } from './utils/react18-polyfill'
|
|
||||||
|
|
||||||
// 应用 React 18 兼容性补丁
|
// 仅在开发环境启用所有修复(幂等,不影响其他功能)
|
||||||
setupReact18Polyfill()
|
|
||||||
// 开发期:剔除会透传到原生 DOM 的非标准属性(localeCode/defaultCurrency/showCurrencySymbol)
|
|
||||||
setupDevSanitizeDOMProps()
|
|
||||||
|
|
||||||
// 仅在开发环境过滤特定第三方库产生的已知无害告警
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
const shouldSuppress = (msg: unknown) => {
|
setupReactDevFixes()
|
||||||
if (typeof msg !== 'string') return false
|
|
||||||
// semi-ui 某些组件链路在 React 18 下把非标准属性透传到 DOM,触发告警
|
|
||||||
if (msg.includes('React does not recognize the `localeCode` prop on a DOM element')) return true
|
|
||||||
if (msg.includes('React does not recognize the `defaultCurrency` prop on a DOM element')) return true
|
|
||||||
if (msg.includes('React does not recognize the `showCurrencySymbol` prop on a DOM element')) return true
|
|
||||||
// 浏览器建议表单自动填充
|
|
||||||
if (msg.includes('[DOM] Input elements should have autocomplete attributes')) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const origError = console.error
|
|
||||||
console.error = (...args: any[]) => {
|
|
||||||
const msg = args?.[0]
|
|
||||||
if (shouldSuppress(msg)) return
|
|
||||||
origError(...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
const origWarn = console.warn
|
|
||||||
console.warn = (...args: any[]) => {
|
|
||||||
const msg = args?.[0]
|
|
||||||
if (shouldSuppress(msg)) return
|
|
||||||
origWarn(...args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
|||||||
@ -251,7 +251,7 @@ export default function FlowList() {
|
|||||||
confirmLoading={editing}
|
confirmLoading={editing}
|
||||||
onCancel={() => { setEditOpen(false); setEditRow(null); setEditName(''); editForm.resetFields() }}
|
onCancel={() => { setEditOpen(false); setEditRow(null); setEditName(''); editForm.resetFields() }}
|
||||||
okText="保存"
|
okText="保存"
|
||||||
destroyOnClose
|
destroyOnHidden
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
>
|
>
|
||||||
<Form form={editForm} layout="vertical" preserve={false} initialValues={{ name: editName }}>
|
<Form form={editForm} layout="vertical" preserve={false} initialValues={{ name: editName }}>
|
||||||
@ -273,7 +273,7 @@ export default function FlowList() {
|
|||||||
open={createOpen}
|
open={createOpen}
|
||||||
onOk={handleCreateOk}
|
onOk={handleCreateOk}
|
||||||
onCancel={() => setCreateOpen(false)}
|
onCancel={() => setCreateOpen(false)}
|
||||||
destroyOnClose
|
destroyOnHidden
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
>
|
>
|
||||||
<Form form={createForm} layout="vertical" preserve={false}>
|
<Form form={createForm} layout="vertical" preserve={false}>
|
||||||
|
|||||||
@ -144,7 +144,7 @@ export default function FlowRunLogs() {
|
|||||||
pagination={{ current: page, pageSize, total, onChange: (p, ps) => fetchData(p, ps) }}
|
pagination={{ current: page, pageSize, total, onChange: (p, ps) => fetchData(p, ps) }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Drawer title="运行详情" width={820} open={detailOpen} onClose={closeDetail} destroyOnClose placement="right">
|
<Drawer title="运行详情" width={820} open={detailOpen} onClose={closeDetail} destroyOnHidden placement="right">
|
||||||
{detail && (
|
{detail && (
|
||||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||||
<Descriptions column={2} size="small" bordered>
|
<Descriptions column={2} size="small" bordered>
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export default function Logs() {
|
|||||||
pagination={{ current: page, pageSize, total, onChange: (p, ps) => fetchData(p, ps) }}
|
pagination={{ current: page, pageSize, total, onChange: (p, ps) => fetchData(p, ps) }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Drawer title="日志详情" width={720} open={detailOpen} onClose={closeDetail} destroyOnClose placement="right">
|
<Drawer title="日志详情" width={720} open={detailOpen} onClose={closeDetail} destroyOnHidden placement="right">
|
||||||
{detail && (
|
{detail && (
|
||||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||||
<Descriptions column={1} size="small" bordered>
|
<Descriptions column={1} size="small" bordered>
|
||||||
|
|||||||
@ -1,67 +1,126 @@
|
|||||||
/**
|
/**
|
||||||
* React 18 兼容性补丁 + 开发期告警抑制
|
* React 18 兼容性补丁 + 开发期告警修复
|
||||||
* - 解决第三方库使用旧版 ReactDOM.render API 的问题
|
* - 解决第三方库使用旧版 ReactDOM.render API 的问题
|
||||||
* - 在开发环境下拦截并剔除部分第三方库会误透传到原生 DOM 的非标准属性
|
* - 在开发环境下拦截并剔除部分第三方库会误透传到原生 DOM 的非标准属性
|
||||||
*/
|
*/
|
||||||
import * as ReactDOM from 'react-dom/client';
|
import * as ReactDOM from 'react-dom/client'
|
||||||
import * as React from 'react';
|
import * as React from 'react'
|
||||||
import { unstableSetCreateRoot } from '@flowgram.ai/form-materials';
|
import { unstableSetCreateRoot } from '@flowgram.ai/form-materials'
|
||||||
|
|
||||||
// 设置使用 React 18 createRoot API 的实现
|
// 设置使用 React 18 createRoot API 的实现
|
||||||
export function setupReact18Polyfill() {
|
export function setupReact18Polyfill() {
|
||||||
unstableSetCreateRoot((dom: HTMLElement) => {
|
unstableSetCreateRoot((dom: HTMLElement) => {
|
||||||
const root = ReactDOM.createRoot(dom);
|
const root = ReactDOM.createRoot(dom)
|
||||||
return {
|
return {
|
||||||
render(children: React.ReactNode) {
|
render(children: React.ReactNode) {
|
||||||
root.render(children);
|
root.render(children)
|
||||||
},
|
},
|
||||||
// 将 root.unmount 延迟到当前渲染完成之后再执行,避免 React 的同步卸载告警
|
// 将 root.unmount 延迟到当前渲染完成之后再执行,避免 React 的同步卸载告警
|
||||||
unmount() {
|
unmount() {
|
||||||
const doUnmount = () => root.unmount();
|
const doUnmount = () => root.unmount()
|
||||||
if (typeof queueMicrotask === 'function') {
|
if (typeof queueMicrotask === 'function') {
|
||||||
queueMicrotask(doUnmount);
|
queueMicrotask(doUnmount)
|
||||||
} else if (typeof Promise !== 'undefined') {
|
} else if (typeof Promise !== 'undefined') {
|
||||||
Promise.resolve().then(doUnmount);
|
Promise.resolve().then(doUnmount)
|
||||||
} else {
|
} else {
|
||||||
setTimeout(doUnmount, 0);
|
setTimeout(doUnmount, 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
console.log('React 18 polyfill has been applied');
|
if (typeof console !== 'undefined' && console.debug) {
|
||||||
|
console.debug('React 18 polyfill has been applied')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开发环境:剔除被第三方库误透传到原生 DOM 的非标准属性,防止 React 告警
|
// 开发环境:剔除被第三方库误透传到原生 DOM 的非标准属性,防止 React 告警
|
||||||
export function setupDevSanitizeDOMProps() {
|
export function setupDevSanitizeDOMProps() {
|
||||||
if (!import.meta.env.DEV) return;
|
if (!import.meta.env.DEV) return
|
||||||
const ANY_REACT = React as any;
|
const ANY_REACT = React as any
|
||||||
// 避免重复打补丁
|
// 避免重复打补丁
|
||||||
if (ANY_REACT.__patched_createElement__) return;
|
if (ANY_REACT.__patched_createElement__) return
|
||||||
|
|
||||||
const origCreateElement = React.createElement as any;
|
const origCreateElement = React.createElement as any
|
||||||
const STRIP_KEYS = new Set(['localeCode', 'defaultCurrency', 'showCurrencySymbol']);
|
const STRIP_KEYS = new Set(['localeCode', 'defaultCurrency', 'showCurrencySymbol'])
|
||||||
|
|
||||||
const patchedCreateElement = (type: any, props: any, ...children: any[]) => {
|
const patchedCreateElement = (type: any, props: any, ...children: any[]) => {
|
||||||
if (typeof type === 'string' && props && typeof props === 'object') {
|
if (typeof type === 'string' && props && typeof props === 'object') {
|
||||||
let mutated = false;
|
let mutated = false
|
||||||
const nextProps: any = {};
|
const nextProps: any = {}
|
||||||
for (const key in props) {
|
for (const key in props) {
|
||||||
if (Object.prototype.hasOwnProperty.call(props, key)) {
|
if (Object.prototype.hasOwnProperty.call(props, key)) {
|
||||||
if (STRIP_KEYS.has(key)) {
|
if (STRIP_KEYS.has(key)) {
|
||||||
mutated = true;
|
mutated = true
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
nextProps[key] = props[key];
|
nextProps[key] = props[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return origCreateElement(type, mutated ? nextProps : props, ...children);
|
return origCreateElement(type, mutated ? nextProps : props, ...children)
|
||||||
}
|
}
|
||||||
return origCreateElement(type, props, ...children);
|
return origCreateElement(type, props, ...children)
|
||||||
};
|
}
|
||||||
|
|
||||||
(React as any).createElement = patchedCreateElement;
|
;(React as any).createElement = patchedCreateElement
|
||||||
ANY_REACT.__patched_createElement__ = true;
|
ANY_REACT.__patched_createElement__ = true
|
||||||
if (typeof console !== 'undefined' && console.debug) {
|
if (typeof console !== 'undefined' && console.debug) {
|
||||||
console.debug('[DEV] React.createElement patched to sanitize DOM props');
|
console.debug('[DEV] React.createElement patched to sanitize DOM props')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 开发环境:抑制特定第三方库产生的已知无害告警(仅字符串匹配,不影响其他日志)
|
||||||
|
export function setupDevConsoleSuppression() {
|
||||||
|
if (!import.meta.env.DEV) return
|
||||||
|
const anyConsole: any = console as any
|
||||||
|
if (anyConsole.__patched_console__) return
|
||||||
|
|
||||||
|
const shouldSuppressArgs = (args: any[]): boolean => {
|
||||||
|
try {
|
||||||
|
const joined = args
|
||||||
|
.map((a) => {
|
||||||
|
if (typeof a === 'string') return a
|
||||||
|
if (a instanceof Error && a.message) return a.message
|
||||||
|
try {
|
||||||
|
return JSON.stringify(a)
|
||||||
|
} catch {
|
||||||
|
return String(a)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join(' ')
|
||||||
|
|
||||||
|
const hitsReactUnrecognized = joined.includes('React does not recognize the')
|
||||||
|
const hitsKnownKeys = joined.includes('localeCode') || joined.includes('defaultCurrency') || joined.includes('showCurrencySymbol')
|
||||||
|
|
||||||
|
if (hitsReactUnrecognized && hitsKnownKeys) return true
|
||||||
|
if (joined.includes('[DOM] Input elements should have autocomplete attributes')) return true
|
||||||
|
return false
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const origError = console.error
|
||||||
|
const origWarn = console.warn
|
||||||
|
|
||||||
|
console.error = (...args: any[]) => {
|
||||||
|
if (shouldSuppressArgs(args)) return
|
||||||
|
origError(...args)
|
||||||
|
}
|
||||||
|
console.warn = (...args: any[]) => {
|
||||||
|
if (shouldSuppressArgs(args)) return
|
||||||
|
origWarn(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
anyConsole.__patched_console__ = true
|
||||||
|
if (typeof console !== 'undefined' && console.debug) {
|
||||||
|
console.debug('[DEV] console.* patched to suppress known harmless warnings')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 聚合:一次性启用所有开发期修复(幂等)
|
||||||
|
export function setupReactDevFixes() {
|
||||||
|
if (!import.meta.env.DEV) return
|
||||||
|
setupReact18Polyfill()
|
||||||
|
setupDevSanitizeDOMProps()
|
||||||
|
setupDevConsoleSuppression()
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user