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

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

Message

【React】createPortal - 简单的Message和Modal组件_第1张图片

Message.tsx

import { type FC } from "react";
import { createRoot, type Root } from "react-dom/client";
import "./Message.css";

export const Message: FC = () => {
  return 
提示信息
; }; 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(); 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 (
    <>
      
); } export default App;

App.css

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

Modal

【React】createPortal - 简单的Message和Modal组件_第2张图片
Modal.tsx

import React, { type FC } from 'react'
import { createRoot } from 'react-dom/client'
import './Modal.css'

export const Modal: FC = () => {
  return (
    

标题

内容

) } window.onShowModal = () => { const modal = document.createElement('div') modal.className = 'modal' document.body.appendChild(modal) const root = createRoot(modal) root.render() } 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(
    

标题

内容

, document.body ); }; window.onShowModal = () => { const modal = document.createElement("div"); modal.className = "modal"; document.body.appendChild(modal); const root = createRoot(modal); root.render(); }; 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;
}

你可能感兴趣的:(React,及其周边生态,react.js,前端,前端框架)