自定义组件是每个前端开发者必备的技能。我们在使用现有框架时难免有一些超乎框架以处的特别的需求,比如关于弹窗,每个应用都会用到,但是有时我们使用的框架中提供的弹窗功能也是功能有限,无法满足我们的应用需求,今天 我来讲一下在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();
你看一个基本的弹窗就设计完了。