React/Nextjs使用createPortal报错Target container is not a DOM element求助
错误原因
ReactDOM.createPortal第二个参数要求传入真实存在的DOM元素对象,你当前传入的是useEffect钩子的返回值,useEffect默认返回undefined或者清理函数,永远不可能返回DOM节点,直接导致portal找不到挂载容器。- 你在
_document.js中将id为message的容器写在了<body>标签外部,属于非法的HTML结构,浏览器解析时会自动调整节点位置,也可能导致节点获取失败。 - Next.js 存在服务端渲染阶段,服务端运行时不存在
document对象,直接调用document.getElementById会导致服务端报错,就算写法正确也会在构建/首屏渲染时报错。
修正代码
1. 修正 _document.js
把message容器移到body内部:
class MyDocument extends Document { render() { return ( <Html> <Head /> <body> <Main /> <DeferNextScript /> {/* message容器移到body标签内部 */} <div id="message"></div> </body> </Html> ); } } export default MyDocument;
2. 修正 message.js
调整portal渲染逻辑,等组件客户端挂载完成后再获取DOM节点渲染:
import { useContext, useEffect, useState, useRef } from 'react' import ReactDOM from 'react-dom' function Message(props) { const messageCtx = useContext(MessageBannerContext); const [isClientMounted, setIsClientMounted] = useState(false) const messageContainerRef = useRef(null) // 仅在客户端挂载后获取DOM节点,避免服务端渲染报错 useEffect(() => { messageContainerRef.current = document.getElementById('message') setIsClientMounted(true) }, []) // 未挂载完成、未拿到容器节点时不渲染portal if (!isClientMounted || !messageContainerRef.current) return null return ReactDOM.createPortal( <div> <p>Message</p> <button>More info</button> <button onClick={messageCtx.hideMessage}>Ok</button> </div>, messageContainerRef.current ); } export default Message;
内容的提问来源于stack exchange,提问作者RandomDeveloper
相关产品推荐
相关产品推荐

