在 react 创建 modal

方案 1 普通使用

在组件中直接创建 modal 显示:

const {useState, useMemo} = React
const useModal = () => {
const [visible, setVisible] = useState(false)
return useMemo(() => {
return {
visible,
show: () => {
setVisible(true)
},
close: () => {
setVisible(false)
}
}
}, [visible])
}
function App() {
const modal = useModal()
return <>
<button onClick={modal.show}>显示 modal
</button>
<Modal visible={modal.visible} close={modal.close}>qwerty</Modal>
</>
}
ReactDOM.render(<App/>,
document.getElementById('root')
);

这里我使用了 hooks 的方案来创建 modal

modal 组件:

const stopPropagation = ev => ev.stopPropagation()
/**
* 点击 x 和 mask 的时候可以关闭 modal
* @param props
* @return {*|JSX.Element}
* @constructor
*/
function Modal1(props) {
return props.visible && <div onClick={props.close} className="modal-container">
<div className="modal-box" onClick={stopPropagation}>
<div className="modal-title">
<span>模态框</span>
<span className="modal-close-icon" onClick={props.close}>x</span>
</div>
{props.children}
</div>
</div>
}

添加了整个 div 的 click 事件, 使得其在点击空白区域时可以触发 close 事件

使用这种方法添加 modal, 优点就是使用简单
但是需要在当前组件引用 modal, 不能缓存 modal

方案 2 指令式的调用

代码:

const {useRef} = React
const {body} = document
const show = () => {
const Dom = document.createElement('div')
Dom.classList.add('modal-container')
body.appendChild(Dom)
const close = () => {
ReactDOM.unmountComponentAtNode(Dom)
body.removeChild(Dom)
}
ReactDOM.render(<Modal close={close}/>, Dom)
}
const showV2 = () => {
let Dom
if (!showV2.cacheDom) {
let _Dom = document.createElement('div')
_Dom.classList.add('modal-container')
body.appendChild(_Dom)
showV2.cacheDom = _Dom
Dom = _Dom
} else {
Dom = showV2.cacheDom
Dom.style.display = 'block'
}
const close = () => {
Dom.style.display = 'none'
}
ReactDOM.render(<Modal close={close}/>, Dom)
}
showV2.cacheDom = null
function App() {
return <>
<button onClick={show}>显示 modal
</button>
<button onClick={showV2}>显示 modal (缓存 modal)
</button>
</>
}
ReactDOM.render(<App/>,
document.getElementById('root')
);

灵活使用 react-dom 的 api ReactDOM.renderReactDOM.unmountComponentAtNode
通过 api 给 dom 挂载(解除)我们需要的组件
这样使用有 2 个主要的优点:

  1. 指令式调用, 不用在 组件里引入 modal 再声明一个状态值和显示/隐藏的方法来控制他,这些冗余的操作(虽然这些东西可以通过一些方案来解决,但是依旧很麻烦)
  2. 可以缓存 modal, 在第二次调用时 只需要改变 display 即可

在很多组件库里,这种写法是被经常用到的, 比如 ant-design 里的 message 和 modal(部分)

方案 3 使用 react 的 api 强化方案1

这里需要说到的是 react 的 api : createPortal, 官网链接: https://zh-hans.reactjs.org/docs/portals.html

来自官网的一句简单介绍: Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案

其实用起来和方案一 是差不多的,关键在于 modal 的代码不一样:

const {body} = document
function Modal(props) {
return ReactDOM.createPortal(
<div className="modal-container" style={{display: props.visible ? 'block' : 'none'}}>
<div className="modal-box">
<div className="modal-title">
<span>模态框</span>
<span className="modal-close-icon" onClick={props.close}>x</span>
</div>
{props.children}
</div>
</div>
, body)
}

优点

  1. 事件冒泡, 在 Modal 外面尝试包裹一层 div, 再添加 click 事件监听:

    <div onClick={ev => {
    console.log(ev, ev.target)
    }}>
    <Modal visible={modal.visible} close={modal.close}/>
    </div>

    在某些特殊情况下比较方便, 可以将一些事件api暴露出来,并且也不影响内部

  2. 当父组件添加 overflow: hidden 或 z-index 时, modal 并不会被隐藏
    这个优点当然是相对于方案一来说的

总结

总结下 react 里基本就这几种基本方案, 如果有遗漏的地方也麻烦留个言告知下
这里放上这篇文章里的所有代码, 如果有帮助到了你, 麻烦点下 star, 感激不尽
https://github.com/Grewer/JsDemo/tree/master/reactModal

posted @   Grewer  阅读(819)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示