写组件的能力是掂量前端工程师程度的重要指标,不论是根底组件还是业务组件。
笔者在闲暇工夫也喜爱写组件,为了帮忙初学者上手写React组件,同时为了分享我在写组件中的教训和想法,决定开设一个系列,即:入手撸组件系列
,和大家分享一些公共组件和业务组件的实现形式和实现技巧。
作为这个系列的第一篇文章,分享下如何从零到一实现一个折叠面板(Collapse)组件
Collapse根底UI绘制
折叠面板作为一个根底组件,由两局部形成:第一局部是题目区域,第二局部是可折叠区域,点击题目区域能够折叠和开展内容区。为了组件的好看性能够在题目右侧增加一个箭头图标,在开展和折叠的时候使其旋转。
为了升高环境搭建老本,实际采纳create-react-app
环境,创立create-react-app
开发环境异常简略,只须要在装置node
的零碎中执行如下命令
npx create-react-app 项目名称
须要留神的是我的项目名必须为英文,create-react-app会主动为咱们创立一个目录。
我的项目创立实现后,在src
目录下创立名为Collapse.jsx
的文件,输出如下代码:
(初学者能够抉择复制)
import React, { useState } from "react";import "./style.css";const CollapsablePanel = () => { const [isCollapsed, setIsCollapsed] = useState(true); const togglePanel = () => { setIsCollapsed((prevState) => !prevState); }; return ( <div className="wrapper"> <div className="pannel" onClick={togglePanel}> <div className="heading"> <span>Flower Collapse</span> <svg width="20px" height="25px" viewBox="0 0 1024 1024" style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1 45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705"> </path></svg> </div> <div className="content" > <div className="contentInner" > Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </div> </div> </div> </div> );};export default CollapsablePanel;
接着创立名称为style.css
的款式文件
.wrapper { display: flex; justify-content: center; width: 100%; height: 100vh; background-color: rgb(228, 239, 239); padding-top: 40vh;}.pannel { width: 400px; text-align: left;}.heading { background-color: #bfa; border-top-left-radius: 10px; border-top-right-radius: 10px; color: #000; font-size: 20px; line-height: 20px; border: 1px solid rgb(212, 240, 205); padding: 10px 20px; display: flex; align-items: center; justify-content: space-between; cursor: pointer;}.content { font-size: 20px; background: #fff; border: 1px solid #fff; border-top: none; padding: 0 20px; color: #000000; overflow: hidden;}.contentInner { padding: 20px 0;}
创立完以上两个文件后,在index.js
中挂载创立好的Collapse组件:
(原有代码无关紧要,间接删除即可)
import React from 'react';import ReactDOM from 'react-dom/client';import Collapse from './components/Collapse';const root = ReactDOM.createRoot(document.getElementById('root'));root.render( <Collapse />);
创立实现后,应用yarn start
命令启动利用,就能看到绘制好的Collapse组件外观:
实现了Collapse根底UI绘制,回顾一下做了哪些操作:
首先定义了名为isCollapsed
的state,存储组件开展敞开状态,并申明了名为togglePanel
办法,在用户点击题目的时候调用此办法即可实现面板的开展敞开。
接着别离定义了款式名为pannel、heading、content的div容器及与其相干的子容器,并在style.css
中设置了容器的款式。组件中的ICON应用svg标签间接绘制,防止因引入svg包增大组件体积。
内容区开展动画
实现动画的形式有很多,能够应用css的transition属性实现,也可应用React生态品种繁多的动画库。在React生态中,有个十分风行的动画库叫react-spring
,不仅功能强大,而且反对hook形式调用,本文就用这个动画库来实现内容区域开展动画和按钮旋转动画。
装置react-spring
动画库
yarn add react-spring
装置完react-spring
动画库当前,就能够定义方法让spring帮咱们生成动画款式了
const panelContentAnimatedStyle = useSpring({ height: isCollapsed ? 0 : 200, });
接着把内容区域的标签名从<div>改为<animated.div>即可(和useSpring雷同,animated也是react-spring具备的一个对象),并在标签中加上刚刚创立的panelContentAnimatedStyle
:
import React, { useState } from "react";import { useSpring, animated } from "react-spring";import "./style.css";const CollapsablePanel = () => { const [isCollapsed, setIsCollapsed] = useState(true); const togglePanel = () => { setIsCollapsed((prevState) => !prevState); }; const panelContentAnimatedStyle = useSpring({ height: isCollapsed ? 0 : 180, }); return ( <div className="wrapper"> <div className="pannel" onClick={togglePanel}> <div className="heading"> <span>Flower Collapse</span> <svg width="20px" height="25px" viewBox="0 0 1024 1024" style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1 45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705"> </path></svg> </div> <animated.div style={panelContentAnimatedStyle} className="content" > <div className="contentInner" > Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </div> </animated.div> </div> </div> );};export default CollapsablePanel;
点击标题栏能够看到如下成果:
等等~~ 高度是固定的吗?
显然不是!用户在应用Collapse组件的时候,传递的内容不单是文字还有可能是图片或者是任意类型的ReactNode,所以在开展的时候是须要获取content对象的理论高度,获取DOM对象高度的操作有一个库能够帮忙咱们,react-use-measure,这个库不仅能够测量DOM对象的长度和宽度,还能够测量DOM对象间隔浏览器上下左右的地位。react-use-measure
提供了名为useMeasure
的hook,应用形式如下:
const [ref, bounds] = useMeasure();
第一个参数是ref对象,将其绑定到须要测量的DOM对象的ref属性上即可,第二个bounds就是地位对象,蕴含下面提到的所有属性。
持续革新组件代码Collapse.jsx
:
import React, { useState } from "react";import { useSpring, animated } from "react-spring";import useMeasure from 'react-use-measure'import "./style.css";const CollapsablePanel = () => { const [isCollapsed, setIsCollapsed] = useState(true); const [ref, bounds] = useMeasure(); const togglePanel = () => { setIsCollapsed((prevState) => !prevState); }; const panelContentAnimatedStyle = useSpring({ height: isCollapsed ? 0 : bounds.height, }); return ( <div className="wrapper"> <div className="pannel" onClick={togglePanel}> <div className="heading"> <span>Flower Collapse</span> <svg width="20px" height="25px" viewBox="0 0 1024 1024" style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1 45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705"> </path></svg> </div> <animated.div style={panelContentAnimatedStyle} className="content" > <div ref={ref} className="contentInner" > Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quasi aperiam dignissimos eaque deserunt expedita sit accusamus sunt laudantium repellendus nisi! Sit, consequuntur. Tempora, officiis molestiae fuga sit quae aliquid maxime. </div> </animated.div> </div> </div> );};export default CollapsablePanel;
当初的成果:
应用react-use-measure能够十分不便的获取DOM对象的实在高度和在浏览器中的地位,在我的项目中灵活运用能够进步开发效率。
实现箭头图标旋转动画
箭头图标的旋转和内容区域的实现相似,只须要将其标签改成animated.div
,并将useSpring生成的款式对象绑定即可。
生成箭头ICON旋转动画style对象:
const toggleWrapperAnimatedStyle = useSpring({ transform: isCollapsed ? "rotate(0deg)" : "rotate(180deg)", });
svg对象外套一个div,并绑定动画款式:
import React, { useState } from "react";import { useSpring, animated } from "react-spring";import useMeasure from 'react-use-measure'import "./style.css";const CollapsablePanel = () => { const [isCollapsed, setIsCollapsed] = useState(true); const [ref, bounds] = useMeasure(); const togglePanel = () => { setIsCollapsed((prevState) => !prevState); }; const panelContentAnimatedStyle = useSpring({ height: isCollapsed ? 0 : bounds.height, }); const toggleWrapperAnimatedStyle = useSpring({ transform: isCollapsed ? "rotate(0deg)" : "rotate(180deg)", }); return ( <div className="wrapper"> <div className="pannel" onClick={togglePanel}> <div className="heading"> <span>Flower Collapse</span> <animated.div style={toggleWrapperAnimatedStyle}> <svg width="20px" height="25px" viewBox="0 0 1024 1024" style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1 45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705"> </path></svg> </animated.div> </div> <animated.div style={panelContentAnimatedStyle} className="content" > <div ref={ref} className="contentInner" > Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. </div> </animated.div> </div> </div> );};export default CollapsablePanel;
能够看到如下成果:
总结
入手撸组件系列
第一篇文章抉择讲Collapse组件的实现,是因为这个组件的实现简略而且富裕趣味,读着能够领会到写组件的乐趣。一个简略的组件在实现的时候也有可能遇到问题,像如何获取content区域中的高度,就很有代表性。以后React及Vue的生态都异样凋敝,开发者在实现具体需要的时候要可能灵活运用这些开源库,以开发出简洁且功能强大的组件。