如何使用React Portals实现一个功能强大的抽屉组件
本篇内容介绍了"如何使用React Portals实现一个功能强大的抽屉组件"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
正文
在开始组件设计之前希望大家对css3和js有一定的基础,并了解基本的react/vue语法.我们先看看实现后的组件效果:
1. 组件设计思路
按照之前笔者总结的组件设计原则,我们第一步是要确认需求. 一个抽屉(Drawer)组件会有如下需求点:
能控制抽屉是否可见
能手动配置抽屉的关闭按钮
能控制抽屉的打开方向
关闭抽屉时是否销毁里面的子元素(这个问题是工作中频繁遇到的问题)
指定 Drawer 挂载的 HTML 节点, 可以将抽屉挂载在任何元素上
点击蒙层可以控制是否允许关闭抽屉
能控制遮罩层的展示
能自定义抽屉弹出层样式
可以设置抽屉弹出层宽度
能控制弹出层层级
能控制抽屉弹出方向(上下左右)
点击关闭按钮时能提供回调供开发者进行相关操作
需求收集好之后,作为一个有追求的程序员, 会得出如下线框图:
对于react选手来说,如果没用typescript,建议大家都用PropTypes, 它是react内置的类型检测工具,我们可以直接在项目中导入. vue有自带的属性检测方式,这里就不一一介绍了.
通过以上需求分析, 是不是觉得一个抽屉组件要实现这么多功能很复杂呢? 确实有点复杂,但是不要怕,有了上面精确的需求分析,我们只需要一步步按照功能点实现就好了.对于我们常用的table组件, modal组件等其实也需要考虑到很多使用场景和功能点, 比如antd的table组件暴露了几十个属性,如果不好好理清具体的需求, 实现这样的组件是非常麻烦的.接下来我们就来看看具体实现.
2. 基于react实现一个Drawer组件
2.1. Drawer组件框架设计
首先我们先根据需求将组件框架写好,这样后面写业务逻辑会更清晰:
import PropTypes from 'prop-types' import styles from './index.less' /** * Drawer 抽屉组件 * @param {visible} bool 抽屉是否可见 * @param {closable} bool 是否显示右上角的关闭按钮 * @param {destroyOnClose} bool 关闭时销毁里面的子元素 * @param {getContainer} HTMLElement 指定 Drawer 挂载的 HTML 节点, false 为挂载在当前 dom * @param {maskClosable} bool 点击蒙层是否允许关闭抽屉 * @param {mask} bool 是否展示遮罩 * @param {drawerStyle} object 用来设置抽屉弹出层样式 * @param {width} number|string 弹出层宽度 * @param {zIndex} number 弹出层层级 * @param {placement} string 抽屉方向 * @param {onClose} string 点击关闭时的回调 */ function Drawer(props) { const { closable = true, destroyOnClose, getContainer = document.body, maskClosable = true, mask = true, drawerStyle, width = '300px', zIndex = 10, placement = 'right', onClose, children } = props const childDom = () return childDom } export default DrawerX }
有了这个框架,我们来一步步往里面实现内容吧.
2.2 实现visible, closable, onClose, mask, maskClosable, width, zIndex, drawerStyle
之所以要先实现这几个功能,是因为他们实现都比较简单,不会牵扯到其他复杂逻辑.只需要对外暴露属性并使用属性即可. 具体实现如下:
function Drawer(props) { const { closable = true, destroyOnClose, getContainer = document.body, maskClosable = true, mask = true, drawerStyle, width = '300px', zIndex = 10, placement = 'right', onClose, children } = props let [visible, setVisible] = useState(props.visible) const handleClose = () => { setVisible(false) onClose && onClose() } useEffect(() => { setVisible(props.visible) }, [props.visible]) const childDom = ({ !!mask && }) return childDom }{ children } { !!closable && X }
上述实现过程值得注意的就是我们组件设计采用了react hooks技术, 在这里用到了useState, useEffect, 如果大家不懂的可以去官网学习, 非常简单,如果有不懂的可以和笔者交流或者在评论区提问. 抽屉动画我们通过控制抽屉内容的宽度来实现,配合overflow:hidden, 后面我会单独附上css代码供大家参考.
2.3 实现destroyOnClose
destroyOnClose主要是用来清除组件缓存,比较常用的场景就是输入文本,比如当我是的抽屉的内容是一个表单创建页面时,我们关闭抽屉希望表单中用户输入的内容清空,保证下次进入时用户能重新创建, 但是实际情况是如果我们不销毁抽屉里的子组件, 子组件内容不会清空,用户下次打开时开始之前的输入,这明显不合理. 如下图所示:
要想清除缓存,首先就要要内部组件重新渲染,所以我们可以通过一个state来控制,如果用户明确指定了关闭时要销毁组件,那么我们就更新这个state,从而这个子元素也就不会有缓存了.具体实现如下:
function Drawer(props) { // ... let [isDesChild, setIsDesChild] = useState(false) const handleClose = () => { // ... if(destroyOnClose) { setIsDesChild(true) } } useEffect(() => { // ... setIsDesChild(false) }, [props.visible]) const childDom = () return childDom }上述代码中我们省略了部分不相关代码, 主要来关注isDesChild和setIsDesChild, 这个属性用来根据用户传入的destroyOnClose属性俩判断是否该更新这个state, 如果destroyOnClose为true,说明要更新,那么此时当用户点击关闭按钮的时候, 组件将重新渲染, 在用户再次点开抽屉时, 我们根据props.visible的变化,来重新让子组件渲染出来,这样就实现了组件卸载的完整流程.
2.4 实现getContainer
getContainer主要用来控制抽屉组件的渲染位置,默认会渲染到body下, 为了提供更灵活的配置,我们需要让抽屉可以渲染到任何元素下,这样又怎么实现呢? 这块实现我们可以采用React Portals来实现,具体api介绍如下:
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。
具体使用如下:
render() { // `domNode` 是一个可以在任何位置的有效 DOM 节点。 return ReactDOM.createPortal( this.props.children, domNode ); }所以基于这个api我们就能把抽屉渲染到任何元素下了, 具体实现如下:
const childDom = ({ !!mask && }) return getContainer === false ? childDom : ReactDOM.createPortal(childDom, getContainer){ isDesChild ? null : children } { !!closable && X }因为这里getContainer要支持3种情况,一种是用户不配置属性,那么默认就挂载到body下,还有就是用户传的值为false, 那么就为最近的父元素, 他如果传一个dom元素,那么将挂载到该元素下,所以以上代码我们会分情况考虑,还有一点要注意,当抽屉打开时,我们要让父元素溢出隐藏,不让其滚动,所以我们在这里要设置一下:
useEffect(() => { setVisible(() => { if(getContainer !== false && props.visible) { getContainer.style.overflow = 'hidden' } return props.visible }) setIsDesChild(false) }, [props.visible, getContainer])当关闭时恢复逻辑父级的overflow, 避免影响外部样式:
const handleClose = () => { onClose && onClose() setVisible((prev) => { if(getContainer !== false && prev) { getContainer.style.overflow = 'auto' } return false }) if(destroyOnClose) { setIsDesChild(true) } }2.5 实现placement
placement主要用来控制抽屉的弹出方向, 可以从左弹出,也可以从右弹出, 实现过程也比较简单,我们主要要更具属性动态修改定位属性即可,这里我们会用到es新版的新特性,对象的变量属性. 核心代码如下:
这样,无论是上下左右,都可以完美实现了.
2.6 健壮性支持, 我们采用react提供的propTypes工具:
import PropTypes from 'prop-types' // ... Drawer.propTypes = { visible: PropTypes.bool, closable: PropTypes.bool, destroyOnClose: PropTypes.bool, getContainer: PropTypes.element, maskClosable: PropTypes.bool, mask: PropTypes.bool, drawerStyle: PropTypes.object, width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), zIndex: PropTypes.number, placement: PropTypes.string, onClose: PropTypes.func }关于prop-types的使用官网上有很详细的案例,这里说一点就是oneOfType的用法, 它用来支持一个组件可能是多种类型中的一个. 组件相关css代码如下:
.xDrawerWrap { top: 0; height: 100vh; overflow: hidden; .xDrawerMask { position: absolute; left: 0; right: 0; top: 0; bottom: 0; background-color: rgba(0, 0, 0, .5); } .xDrawerContent { position: absolute; top: 0; padding: 16px; height: 100%; transition: all .3s; background-color: #fff; box-shadow: 0 0 20px rgba(0,0,0, .2); .xCloseBtn { position: absolute; top: 10px; right: 10px; color: #ccc; cursor: pointer; } } }"如何使用React Portals实现一个功能强大的抽屉组件"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
抽屉 组件 元素 属性 控制 用户 内容 需求 代码 功能 节点 设计 就是 情况 按钮 方向 复杂 宽度 样式 框架 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 csol2服务器中断 高速光纤网络技术 美国服务器代购 慧通新员工网络安全与隐私考试 美国独立服务器 测绘技术成果管理数据库 db2备份数据库sql1035 成都免费进销存软件开发 智立升软件能在服务器运行吗 公主连接台服不同服务器互通吗 宣扬网络安全正能量 网络安全专业学习计划 最新网络安全法规 四核服务器价格 天象网络技术成都青羊 海量存储数据库有哪些 ibm服务器不能开机了提示 网络安全法腾讯 低功耗无线传感网络技术 银川网络安全保卫支队 服务器怎么检索硬盘信息 mc服务器怎么说话 四川惠普服务器维修费用 数据库6.0计时器的做法 b站学网络安全 光明区数据网络技术开发咨询报价 网络技术工程师咨询 c 备份还原数据库 网络安全防火墙设计与实现 张家口聊天软件开发诚信公司