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 是字符串。
相关推荐
Passion不晚2 小时前
Vue vs React vs Angular 的对比和选择
vue.js·react.js·前端框架·angular.js
光影少年11 小时前
usemeno和usecallback区别及使用场景
react.js
吕彬-前端15 小时前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白15 小时前
react hooks--useCallback
前端·react.js·前端框架
恩婧15 小时前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
程序员小杨v116 小时前
如何使用 React Compiler – 完整指南
前端·react.js
谢尔登18 小时前
Babel
前端·react.js·node.js
卸任18 小时前
使用高阶组件封装路由拦截逻辑
前端·react.js
清汤饺子20 小时前
实践指南之网页转PDF
前端·javascript·react.js
霸气小男21 小时前
react + antDesign封装图片预览组件(支持多张图片)
前端·react.js