More or Less 段落组件封装

根据同事封装的一个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);
  };

解释:

  • 导入组件所需的库和依赖项。
  • 设置具有默认属性(classNamelinetype)和 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 是字符串。
相关推荐
熊的猫37 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
小牛itbull5 小时前
ReactPress:重塑内容管理的未来
react.js·github·reactpress
FinGet16 小时前
那总结下来,react就是落后了
前端·react.js
王解19 小时前
Jest项目实战(2): 项目开发与测试
前端·javascript·react.js·arcgis·typescript·单元测试
AIoT科技物语2 天前
免费,基于React + ECharts 国产开源 IoT 物联网 Web 可视化数据大屏
前端·物联网·react.js·开源·echarts
初遇你时动了情2 天前
react 18 react-router-dom V6 路由传参的几种方式
react.js·typescript·react-router
番茄小酱0012 天前
ReactNative中实现图片保存到手机相册
react native·react.js·智能手机
王解2 天前
Jest进阶知识:深入测试 React Hooks-确保自定义逻辑的可靠性
前端·javascript·react.js·typescript·单元测试·前端框架
小牛itbull2 天前
ReactPress—基于React的免费开源博客&CMS内容管理系统
前端·react.js·开源·reactpress
~甲壳虫2 天前
react中得类组件和函数组件有啥区别,怎么理解这两个函数
前端·react.js·前端框架