自定義組件是每個前端開發者必備的技能。我們在使用現有框架時難免有一些超乎框架以處的特別的需求,比如關于彈窗,每個應用都會用到,但是有時我們使用的框架中提供的彈窗功能也是功能有限,無法滿足我們的應用需求,今天 我來講一下在React中如何自定義各種樣式的彈窗。相信通過這篇文章,你能在自定義組件方面技能有一個質的提升。相關的知識能夠掌握的更加牢固。
先看最終的效果:
首先本實例都是在MUI及基礎上設計的,樣式部分我使用了emotion-react
。
彈窗的設計有兩種方案,一種是直接把彈窗組件嵌入到頁面中,用的時候讓它直接顯示或關閉即可,一般的UI框架都是采用這種方法。這種使用方法有一個好處是不受瀏覽器插件的影響,尤其是廣告攔截插件的影響。但使用上有點不方便,必須要在使用彈窗的組件中加入這個彈窗,使用上不太方便。另一種方案是動態創建彈窗組件,在使用的時候直接alert
就可以了。就像使用js原生的彈窗一樣簡單。本篇文章就是圍繞這種設計思路設計一個優雅的彈窗組件。
彈窗遮罩
遮罩很簡單,就一個div
, 在MUI里就是一個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;`;
這是遮罩的樣式,我直接賦于一個Box就好了。
/** @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>)
}
我們再來臨時創建一個簡單的彈窗主體
/** @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}><Boxcss={titleCss}className=".modelHandler">這是標題</Box><Box css={modelContentCss}>這是彈窗內容</Box></Box></Box>);
};export default Modal;
這是一個簡單的彈窗,如何讓它彈出來呢。我的想法是動態的把這個彈窗插入到document
的body
,
// 創建一個div容器,作為彈窗的根節點
const modelContainer = document.createElement("div");// 將div容器添加到body中
document.body.appendChild(modelContainer);
上面我只是創建了一個dom
節點,但我們必須把這個dom
節點添加到React中才能真實的渲染出來。
// 創建一個根節點
const modelRoot = ReactDOM.createRoot(modelContainer);
然后渲染出來
modelRoot.render(<Model />
);
這樣就在body中插入了model組件了,并且能渲染出來。當然能掛載我們還要有卸載才行。很簡單
// 卸載事件
const unmountEvent = () => {modelContainer.remove();
}
我們將上面的彈窗方法整合成一個hook就好了,這樣調用起來就相當的哇塞了。
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(<ModelonClose={unmountEvent}{...configure}>{configure.component || null}</Model>);}}
我們把卸載事件傳遞給了Model
,在遮罩點擊事件上調用,這樣就能開發彈窗也能關閉彈窗。
我們這樣調用就好了:
const alert = useAlert();
alert();
你看一個基本的彈窗就設計完了。