当前位置: 首页 > backend >正文

【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;
}
http://www.xdnf.cn/news/8776.html

相关文章:

  • JAVA集合(含List、Map、Set)(超详细版)
  • 阿里云国际版香港轻量云服务器:CN2 GIA加持,征服海外网络的“速度与激情”!
  • 搭建 C/C++_CMake_Boost_git 开发环境
  • 【最新版】Arduino IDE的安装入门Demo
  • 异步编程与axios技术
  • 代码随想录算法训练营 Day53 图论Ⅳ 字符串接龙 有向图 岛屿周长
  • C#索引器详解:让对象像数组一样被访问
  • 自用git记录
  • Java反射机制详细笔记
  • 项目管理学习-CSPM4(2)
  • 代码随想录第42天:图论3
  • 嵌入式硬件---施密特触发器单稳态触发器多谐振荡器
  • 【Excel VBA 】窗体控件分类
  • 【TDengine源码阅读】举例说明pthread_once_t和PTHREAD_ONCE_INIT
  • STM32 输出比较输出PWM控制呼吸灯小实验(2种实现 铁头山羊与江协科技)
  • Ansible安装
  • C++面向对象编程实战:继承与派生全解析
  • A2A与MCP:差异、协同及企业级应用解析
  • 实战设计模式之访问者模式
  • Javase 基础加强 —— 07 File
  • 云原生安全基石:Linux进程隔离技术详解
  • 2025最新智能优化算法:野燕麦优化算法(Animated Oat Optimization Algorithm, AOO),MATLAB代码
  • JavaSE核心知识点03高级特性03-04(Lambda表达式)
  • 产品迭代与放弃的判断:MVP、PMF 与 Scale Fit 的三重验证
  • VS编码访问Mysql数据库
  • 数据库范式
  • 易贝平台关键字搜索技术深度解析
  • Lesson 21 Mad or not
  • 2024 CKA模拟系统制作 | Step-By-Step | 4、题目搭建-权限控制RBAC
  • 数据库MySQL进阶