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 是字符串。
相关推荐
伟笑1 小时前
React 的常用钩子函数在Vue中是如何设计体现出来的。
前端·react.js
FogLetter2 小时前
React组件开发进阶:本地存储与自定义Hooks的艺术
前端·javascript·react.js
AliciaIr5 小时前
深入React事件机制:解密“合成事件”与“事件委托”的底层奥秘
javascript·react.js
心.c6 小时前
后台管理系统-权限管理
javascript·react.js·github
杨进军7 小时前
实现 React 函数组件渲染
前端·react.js·前端框架
归于尽7 小时前
被 50px 到 200px 的闪烁整破防了?useLayoutEffect 和 useEffect 的区别原来在这
前端·react.js
杨进军7 小时前
实现 React Fragment 节点渲染
前端·react.js·前端框架
杨进军7 小时前
实现 React 类组件渲染
前端·react.js·前端框架
FogLetter7 小时前
React组件开发之Todos基础:从零打造一个优雅的待办事项应用
前端·javascript·react.js
杨进军7 小时前
实现 React 文本节点渲染
前端·react.js·前端框架