React 模态框的设计(二)

自定义组件是每个前端开发者必备的技能。我们在使用现有框架时难免有一些超乎框架以处的特别的需求,比如关于弹窗,每个应用都会用到,但是有时我们使用的框架中提供的弹窗功能也是功能有限,无法满足我们的应用需求,今天 我来讲一下在React中如何自定义各种样式的弹窗。相信通过这篇文章,你能在自定义组件方面技能有一个质的提升。相关的知识能够掌握的更加牢固。

先看最终的效果:

首先本实例都是在MUI及基础上设计的,样式部分我使用了emotion-react。

弹窗的设计有两种方案,一种是直接把弹窗组件嵌入到页面中,用的时候让它直接显示或关闭即可,一般的UI框架都是采用这种方法。这种使用方法有一个好处是不受浏览器插件的影响,尤其是广告拦截插件的影响。但使用上有点不方便,必须要在使用弹窗的组件中加入这个弹窗,使用上不太方便。另一种方案是动态创建弹窗组件,在使用的时候直接alert就可以了。就像使用js原生的弹窗一样简单。本篇文章就是围绕这种设计思路设计一个优雅的弹窗组件。

弹窗遮罩

遮罩很简单,就一个div, 在MUI里就是一个Box组件。

javascript 复制代码
const maskCss = css`
        position: fixed;
        background-color: rgba(0,0,0,0.6);
        border-radius: 5px;
        top: 0px;
        left: 0px;
        width: 100%;
        height: 100%;
        overflow: hidden;
        z-index: 999;
        display: flex;
        justify-content: center;
        align-items: center;
    `;

这是遮罩的样式,我直接赋于一个Box就好了。

javascript 复制代码
/** @jsxImportSource @emotion/react */
import { css, jsx, keyframes } from '@emotion/react'
import { useState, useRef, useEffect, useCallback } from 'react';
import Box from '@mui/material/Box';

export default function Model(props) {
  return (
    <Box css={maskCss}>
    </Box>
  )
}

我们再来临时创建一个简单的弹窗主体

javascript 复制代码
/** @jsxImportSource @emotion/react */
import { css, jsx, keyframes } from '@emotion/react'
import React, { useState } from 'react';
import Box from '@mui/material/Box';

const maskCss = css`
        position: fixed;
        background-color: rgba(0,0,0,0.6);
        border-radius: 5px;
        top: 0px;
        left: 0px;
        width: 100%;
        height: 100%;
        overflow: hidden;
        z-index: 999;
        display: flex;
        justify-content: center;
        align-items: center;
    `;

const modelCss = css`
        position: relative;
        background-color: white;
        border: 1px solid #ccc;
        border-radius: 5px;
        overflow: hidden;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
        width: 400px;
        height: 300px;
    `

const titleCss = css`
        background-color: #f0f0f0;
        padding: 8px;
        cursor: move;
    `;

const modelContentCss = css`
        padding: 16px;
    `;

const Modal = (props) => {
  	const {onClose} = props;
    const onClick = (e) => { 
        console.log('target:', e.target.className);
    }

    return (
      <Box 
      	css={maskCss}
				onClick = {onClose}
			>
          <Box css={modelCss}>
              <Box
                  css={titleCss}
                  className=".modelHandler"
              >
                  这是标题
              </Box>
              <Box css={modelContentCss}>
                  这是弹窗内容
              </Box>
          </Box>
    	</Box>
    );
};

export default Modal;

这是一个简单的弹窗,如何让它弹出来呢。我的想法是动态的把这个弹窗插入到document的body,

javascript 复制代码
// 创建一个div容器,作为弹窗的根节点
const modelContainer = document.createElement("div");

// 将div容器添加到body中
document.body.appendChild(modelContainer);

上面我只是创建了一个dom节点,但我们必须把这个dom节点添加到React中才能真实的渲染出来。

javascript 复制代码
// 创建一个根节点
const modelRoot = ReactDOM.createRoot(modelContainer);

然后渲染出来

javascript 复制代码
modelRoot.render(
    <Model />
);

这样就在body中插入了model组件了,并且能渲染出来。当然能挂载我们还要有卸载才行。很简单

javascript 复制代码
// 卸载事件
const unmountEvent = () => {
    modelContainer.remove();
}

我们将上面的弹窗方法整合成一个hook就好了,这样调用起来就相当的哇塞了。

javascript 复制代码
import ReactDOM from 'react-dom/client';
import Model from './_Model';

//可高度自定义的统一弹窗。
export default function useAlert() {
    return (configure) => {
            // 创建一个div容器,作为弹窗的根节点
            const modelContainer = document.createElement("div");

            // 将div容器添加到body中
            document.body.appendChild(modelContainer);

            // 创建一个根节点
            const modelRoot = ReactDOM.createRoot(modelContainer);

            // 卸载事件
            const unmountEvent = () => {
                modelContainer.remove();
            }

            modelRoot.render(
                <Model
                    onClose={unmountEvent}
                    {...configure}
                >
                    {configure.component || null}
                </Model>
            );
        }
    
}

我们把卸载事件传递给了Model,在遮罩点击事件上调用,这样就能开发弹窗也能关闭弹窗。

我们这样调用就好了:

javascript 复制代码
const alert = useAlert();
alert();

你看一个基本的弹窗就设计完了。

相关推荐
10年前端老司机40 分钟前
Next.js+LangGraph.js+ 简历工具AI Agent完整落地
前端·langchain·agent
IT_陈寒5 小时前
Java中equals方法比了个寂寞?原来这才是正确的重写姿势
前端·人工智能·后端
默_笙5 小时前
🚓 分诊台与拆题术:让 RAG 学会判断和规划
前端·javascript
CopyCode5 小时前
用 AI 迁项目有多爽?我把 Webpack 迁 Vite 的全过程记下来了
前端·架构
去伪存真5 小时前
Electron 自动化发布指南:GitHub Actions 跨平台打包全纪录
前端·electron
计算机魔术师5 小时前
OpenAI智能体失控闯进美国政府网站,53张用户图片外泄背后
前端
颜进强5 小时前
14 · NestJS ExecutionContext 执行上下文:守卫、拦截器、过滤器拿到的"同一个 context",为什么能力不一样?
前端·后端·ai编程
程序员Flycan5 小时前
🚀 跨域终结者:前端代理服务器(Proxy)原理解析与配置总结
前端
怕浪猫5 小时前
顶级模型一句话,AI 写出了能玩的 QQ飞车
前端·面试·github
huakoh5 小时前
MCP 报错分不清?先看响应里是 result 还是 error
前端