React弹窗组件实现

弹窗组件通常伴随着一些通用的功能需求:

可以将这些功能封装成一个 ModalWrapper 组件

组件实现

此处样式使用 tailwindcss 的类名进行设置, 可以根据实际场景改为 css 实现

// ModalWrapper.tsx
 
import { ReactNode, MouseEvent, useEffect } from 'react'
import { useRef } from 'react'
 
const ModalWrapper = ({ children, onBgClick }: { children: ReactNode, onBgClick?: VoidFunction }) => {
    const ref = useRef<HTMLDivElement | null>(null)
 
    const handleClick = (ev: MouseEvent<HTMLDivElement>) => {
        if (ev.target === ref.current) {
            ev.stopPropagation()
            ev.preventDefault()
            onBgClick?.()
        }
    }
 
    useEffect(() => {
        // memorize the original overflow style of body
        const mem = document.body.style.overflow
 
        // disable scroll when modal is shown
        document.body.style.overflow = 'hidden'
 
        return () => {
            // restore the original overflow style of body
            document.body.style.overflow = mem
        }
    }, [])
 
    return (
        <div ref={ ref } className={
            'fixed z-[100] w-full h-full top-0 left-0 bg-[#00000080] flex flex-col items-center justify-center'
        } onClick={ handleClick }>
            { children }
        </div>
    )
}
 
export {
    ModalWrapper,
}

场景使用

// ModalExample.tsx
 
import { useState } from "react";
 
const ModalExample = () => {
    const [ showModal, setShowModal ] = useState(false)
 
    return (
        <div>
            <button onClick={ () => setShowModal(true) }>show modal</button>
 
            {
                showModal && <ModalWrapper onBgClick={ () => alert('click on mask') }>
                    <div className={ 'w-[100px] h-[100px] bg-white' } onClick={ () => setShowModal(false) }>
                        click to close modal
                    </div>
                </ModalWrapper>
            }
 
            <div className={ 'w-full h-[1000px]' }>
                mock a long content here
            </div>
        </div>
    )
}