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 是字符串。
相关推荐
郝开4 小时前
扩展:React 项目执行 yarn eject 后的 package.json 变化详解及参数解析
react.js·前端框架·react
Coding的叶子15 小时前
Node.js 安装 + React Flow 快速入门:环境安装与项目搭建
react.js·node.js·react flow·fgai·react agent
刺客-Andy15 小时前
React 第四十一节Router 中 useActionData 使用方法案例以及注意事项
前端·react.js·前端框架
肠胃炎15 小时前
React事件机制
前端·javascript·react.js
帅帅哥的兜兜17 小时前
next.js实现项目搭建
前端·react.js·next.js
海上彼尚20 小时前
秒删node_modules[无废话版]
vue.js·react.js
程序猿阿伟20 小时前
《数字分身进化论:React Native与Flutter如何打造沉浸式虚拟形象编辑》
flutter·react native·react.js
果冻kk21 小时前
【实战教程】从零实现DeepSeek AI多专家协作系统 - Spring Boot+React打造AI专家团队协作平台
人工智能·spring boot·react.js
进取星辰21 小时前
28、动画魔法圣典:Framer Motion 时空奥义全解——React 19 交互动效
前端·react.js·交互
Zero1017131 天前
【详解pnpm、npm、yarn区别】
前端·react.js·前端框架