欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 【React】createPortal - 简单的Message和Modal组件

【React】createPortal - 简单的Message和Modal组件

2025/5/30 2:23:57 来源:https://blog.csdn.net/XiugongHao/article/details/148201252  浏览:    关键词:【React】createPortal - 简单的Message和Modal组件

来源:小满zs React教程学习笔记。

Message

在这里插入图片描述

Message.tsx

import { type FC } from "react";
import { createRoot, type Root } from "react-dom/client";
import "./Message.css";export const Message: FC = () => {return <div>提示信息</div>;
};interface MessageItem {MessageContainer: HTMLElement;root: Root;
}const queue: MessageItem[] = [];window.onShow = () => {// 1. 创建消息容器const messageContainer = document.createElement("div");messageContainer.className = "message-container";messageContainer.style.top = `${queue.length * 50}px`;document.body.appendChild(messageContainer);// 2. 容器关联Message组件 将容器注册为根节点const root = createRoot(messageContainer);root.render(<Message />);queue.push({MessageContainer: messageContainer,root: root,});// 3. 设置定时器 定时移除容器setTimeout(() => {const item = queue.find((item) => item.MessageContainer === messageContainer)!;item.root.unmount();document.body.removeChild(messageContainer);queue.splice(queue.indexOf(item), 1);}, 2000);
};// ts 声明扩充
declare global {interface Window {onShow: () => void;}
}

Message.css

.message-container {width: 200px;height: 40px;color: #18181d;line-height: 40px;text-align: center;border-radius: 10px;position: fixed;top: 10px;left: 50%;transform: translateX(-50%);background-color: #eee;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

main.ts

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./components/Message/Message.tsx";
import "./components/Modal/Modal.tsx";createRoot(document.getElementById("root")!).render(<StrictMode><App /></StrictMode>
);

App.tsx

import "./App.css";
function App() {return (<><div className="app-container"><button onClick={() => window.onShow()}>显示消息</button><button onClick={() => window.onShowModal()}>显示弹窗</button></div></>);
}export default App;

App.css

.app-container {display: flex;flex-direction: column;align-items: center;justify-content: center;height: 100vh;
}

Modal

在这里插入图片描述
Modal.tsx

import React, { type FC } from 'react'
import { createRoot } from 'react-dom/client'
import './Modal.css'export const Modal: FC = () => {return (<div className='modal'><header className='header'><h3>标题</h3></header><main className='main'><p>内容</p></main><footer className='footer'><button>取消</button><button>确定</button></footer></div>)
}window.onShowModal = () => {const modal = document.createElement('div')modal.className = 'modal'document.body.appendChild(modal)const root = createRoot(modal)root.render(<Modal />)
}declare global {interface Window {onShowModal: () => void}
}

Modal.css

.modal {width: 300px;height: 150px;border-radius: 10px;border: 1px solid #ccc;padding: 10px;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 1000;background-color: #fff;
}.header {height: 30px;border-bottom: 1px solid #ccc;
}.main {height: 80px;
}.footer {height: 30px;border-top: 1px solid #ccc;display: flex;justify-content: flex-end;
}

createPortal

但是 position: fixed,会有很多问题,在默认的情况下他是根据浏览器视口进行定位的,但是如果父级设置了transform、perspective、filter 或 backdrop-filter 属性非 none 时,他就会相对于父级进行定位,这样就会导致Modal组件定位不准确(他不是一定按照浏览器视口进行定位),所以不推荐使用。

所以此时我们可以使用 createPortal 这个 API 将其指定挂载到某个位置。

createPortal 
传参:children:要渲染的组件domNode:要渲染到的DOM位置key?:可选,用于唯一标识要渲染的组件
返回值:返回一个React元素(即jsx),这个元素可以被React渲染到DOM的任意位置
应用场景:弹窗下拉框全局提示全局遮罩全局Loading

修改后的 Modal 为:

Modal.tsx

import { type FC } from "react";
import { createPortal } from "react-dom";
import { createRoot } from "react-dom/client";
import "./Modal.css";export const Modal: FC = () => {return createPortal(<div className="modal"><header className="header"><h3>标题</h3></header><main className="main"><p>内容</p></main><footer className="footer"><button>取消</button><button>确定</button></footer></div>,document.body);
};window.onShowModal = () => {const modal = document.createElement("div");modal.className = "modal";document.body.appendChild(modal);const root = createRoot(modal);root.render(<Modal />);
};declare global {interface Window {onShowModal: () => void;}
}

Modal.css

.modal {width: 300px;height: 150px;border-radius: 10px;border: 1px solid #ccc;padding: 10px;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 1000;background-color: #fff;position: absolute;
}.header {height: 30px;border-bottom: 1px solid #ccc;
}.main {height: 80px;
}.footer {height: 30px;border-top: 1px solid #ccc;display: flex;justify-content: flex-end;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词