根据同事封装的一个more or less段落展示组件,我在其上面加上了注释,然后对每段代码都进行了详细的注解,使其开箱即用。
整体代码
jsx
import React, { useState, useEffect, useRef } from "react";
import "./index.less";
import intl from "react-intl-universal";
import ResizeObserver from "resize-observer-polyfill";
import classnames from "classnames";
import PropTypes from "prop-types";
const PhaseFold = ({ className = '', line = 5, type, children }) => {
// State to manage whether to show the fold button and whether the content is folded
const [showFold, setShowFold] = useState(false);
const [isFold, setIsFold] = useState(true);
// Reference for the content element to observe its size changes
const contentRef = useRef(null);
// Click handler for the fold button
const handleClick = () => {
setIsFold(!isFold);
};
useEffect(() => {
// Function to bind the ResizeObserver to the content element
const bindResizeObserver = (element) => {
if (element) {
// Create a new ResizeObserver
const resizeObserver = new ResizeObserver(() => {
const { clientHeight, scrollHeight } = element;
// Update the showFold state based on the content size
setShowFold(scrollHeight > clientHeight);
});
// Start observing the content element
resizeObserver.observe(element);
// Cleanup function to disconnect the observer when component unmounts
return () => resizeObserver.disconnect();
}
};
// Call the binding function with the content element and save the cleanup function
const resizeObserverCleanup = bindResizeObserver(contentRef.current);
// Cleanup function to be executed on component unmount
return () => {
// Call the cleanup function if it exists
if (resizeObserverCleanup) resizeObserverCleanup();
};
}, [isFold]);
return (
<div className={`${className} phase-fold`}>
{/* Content div with optional text clamping based on the 'line' prop */}
<div
ref={contentRef}
style={{ WebkitLineClamp: line }}
className={classnames("phase-fold-text", { "phase-fold-text__fold": isFold })}
>
{children}
</div>
{/* Fold button displayed conditionally based on state and props */}
{(showFold || !isFold) && (
<div
className={classnames("phase-fold-btn", { "phase-fold-btn__primary": type === 'primary' })}
onClick={handleClick}
role="button"
>
{isFold ? intl.get("button_view_more") : intl.get("button_view_less")}
</div>
)}
</div>
);
};
// PropTypes for component props validation
PhaseFold.propTypes = {
className: PropTypes.string,
line: PropTypes.number,
type: PropTypes.string
};
export default PhaseFold;
分段解释
下面将 PhaseFold
组件分成几个部分,详细解释每个代码部分的作用:
导入语句和组件设置:
jsx
import React, { useState, useEffect, useRef } from "react";
import "./index.less";
import intl from "react-intl-universal";
import ResizeObserver from "resize-observer-polyfill";
import classnames from "classnames";
import PropTypes from "prop-types";
const PhaseFold = ({ className = '', line = 5, type, children }) => {
// 用于管理是否显示折叠按钮和内容是否折叠的状态
const [showFold, setShowFold] = useState(false);
const [isFold, setIsFold] = useState(true);
// 引用内容元素以观察其大小变化
const contentRef = useRef(null);
// 折叠按钮的点击处理函数
const handleClick = () => {
setIsFold(!isFold);
};
解释:
- 导入组件所需的库和依赖项。
- 设置具有默认属性(
className
、line
、type
)和children
属性的函数式组件PhaseFold
。
用于 ResizeObserver 的 useEffect:
jsx
useEffect(() => {
// 用于将 ResizeObserver 绑定到内容元素的函数
const bindResizeObserver = (element) => {
if (element) {
// 创建一个新的 ResizeObserver
const resizeObserver = new ResizeObserver(() => {
const { clientHeight, scrollHeight } = element;
// 根据内容大小更新 showFold 状态
setShowFold(scrollHeight > clientHeight);
});
// 开始观察内容元素
resizeObserver.observe(element);
// 当组件卸载时断开观察器的清理函数
return () => resizeObserver.disconnect();
}
};
// 使用内容元素调用绑定函数并保存清理函数
const resizeObserverCleanup = bindResizeObserver(contentRef.current);
// 当组件卸载时执行的清理函数
return () => {
// 如果存在清理函数,则调用清理函数
if (resizeObserverCleanup) resizeObserverCleanup();
};
}, [isFold]);
解释:
- 使用
useEffect
钩子处理ResizeObserver
的设置和清理。 - 在
useEffect
内部,定义bindResizeObserver
函数以将观察器绑定到内容元素并返回清理函数。 - 观察
isFold
状态的更改以触发useEffect
。 - 通过在组件卸载时断开观察器来确保正确的清理。
渲染 JSX:
jsx
return (
<div className={`${className} phase-fold`}>
{/* 内容 div,根据 'line' 属性进行可选的文本截断 */}
<div
ref={contentRef}
style={{ WebkitLineClamp: line }}
className={classnames("phase-fold-text", { "phase-fold-text__fold": isFold })}
>
{children}
</div>
{/* 根据状态和属性有条件地渲染折叠按钮 */}
{(showFold || !isFold) && (
<div
className={classnames("phase-fold-btn", { "phase-fold-btn__primary": type === 'primary' })}
onClick={handleClick}
role="button"
>
{isFold ? intl.get("button_view_more") : intl.get("button_view_less")}
</div>
)}
</div>
);
};
解释:
- 组件的主要 JSX 结构。
- 根据状态和属性有条件地渲染内容 div,以及折叠按钮。
- 根据
isFold
状态和可选的type
属性应用适当的类名以进行样式设置。
PropTypes:
jsx
// 用于组件属性验证的 PropTypes
PhaseFold.propTypes = {
className: PropTypes.string,
line: PropTypes.number,
type: PropTypes.string
};
export default PhaseFold;
解释:
- 定义
PropTypes
以验证组件接收到的属性类型。 - 确保
className
是字符串,line
是数字,type
是字符串。