antd Modal.configm 中使用 FormattedMessage 报错:Uncaught Error: [React Intl] Could not find required 'intl' object. needs to exist in the component ancestry.
问题复现
在 Modal.confirm 中使用FormattedMessage,示例代码如下:
<Button
onClick={() => {
Modal.confirm({
title: <FormattedMessage id="operation.confirm" />,
onOk: async () => {
await delCtrlData(item.id);
},
});
}}
type="link"
danger
>
<DeleteOutlined />
<FormattedMessage id="operation.delete" />
</Button>
错误提示如下:
Uncaught Error: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
at invariant (mf-dep_vendors-node_modules_react-intl_lib_index_js.37b86786.async.js:17:15)
at invariantIntlContext (mf-dep_vendors-node_modules_react-intl_lib_index_js.37b86786.async.js:1951:66)
at mf-dep_vendors-node_modules_react-intl_lib_index_js.37b86786.async.js:816:71
at updateContextConsumer (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:18756:19)
at beginWork (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:19123:14)
at HTMLUnknownElement.callCallback (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:3954:14)
at Object.invokeGuardedCallbackDev (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:4003:16)
at invokeGuardedCallback (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:4065:31)
at beginWork$1 (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:23968:7)
at performUnitOfWork (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:22780:12)
at workLoopSync (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:22711:5)
at renderRootSync (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:22674:7)
at performSyncWorkOnRoot (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:22297:18)
at scheduleUpdateOnFiber (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:21885:7)
at updateContainer (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:25486:3)
at mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:26025:7
at unbatchedUpdates (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:22435:12)
at legacyRenderSubtreeIntoContainer (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:26024:5)
at Object.render (mf-dep_vendors-node_modules_react-dom_index_js.befe9138.async.js:26107:10)
at mf-dep_vendors-node_modules_antd_es_modal_index_js.c9cbf0ad.async.js:558:46
问题原因
Modal.confirm 是在被调用运行时 使用 ReactDom.render 执行的 Modal 组件渲染,相当于新创建了一个 React 应用,外部 Context 在渲染时已经丢失,所以无法访问到 intl 提供的 FormatMessage 组件。
Modal.confirm 方法的 render 源码如下:
function render({ okText, cancelText, prefixCls: customizePrefixCls, ...props }: any) {
/**
* https://github.com/ant-design/ant-design/issues/23623
*
* Sync render blocks React event. Let's make this async.
*/
setTimeout(() => {
const runtimeLocale = getConfirmLocale();
const { getPrefixCls, getIconPrefixCls } = globalConfig();
// because Modal.config set rootPrefixCls, which is different from other components
const rootPrefixCls = getPrefixCls(undefined, getRootPrefixCls());
const prefixCls = customizePrefixCls || `${rootPrefixCls}-modal`;
const iconPrefixCls = getIconPrefixCls();
ReactDOM.render(
<ConfirmDialog
{...props}
prefixCls={prefixCls}
rootPrefixCls={rootPrefixCls}
iconPrefixCls={iconPrefixCls}
okText={okText || (props.okCancel ? runtimeLocale.okText : runtimeLocale.justOkText)}
cancelText={cancelText || runtimeLocale.cancelText}
/>,
container,
);
});
如何解决
第一种也是相对简单的方式是将 FormattedMessage 组件替换为 intl.formatMessage({id:"message.id"}),这样在外部组件渲染时 API 调用已经执行完成,就不会报错了。
第二种是重新封装一下 Modeal.confirm 方法,使用 react-intl 提供的 injectIntl 方法包一层,将 intl 注入到你组件内部方可使用。