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 是字符串。
相关推荐
局外人LZ5 小时前
前端项目搭建集锦:vite、vue、react、antd、vant、ts、sass、eslint、prettier、浏览器扩展,开箱即用,附带项目搭建教程
前端·vue.js·react.js
H5开发新纪元9 小时前
借助 GitHub Copilot 打造一个完美的 React 消息引用系统:从设计到实现的深度剖析
前端·react.js
哟哟耶耶9 小时前
react-09React生命周期
前端·javascript·react.js
进取星辰10 小时前
13、性能优化:魔法的流畅之道——React 19 memo/lazy
前端·react.js·前端框架
zwjapple10 小时前
React中createPortal 的详细用法
前端·javascript·react.js
小矮马10 小时前
React-组件通信
前端·javascript·react.js
火星思想11 小时前
React如何实现时间切片
前端·react.js
qq_252496399614 小时前
react 子组件暴露,父组件接收
前端·javascript·react.js
zwjapple15 小时前
React 的 useEffect 清理函数详解
前端·react.js·前端框架
前端大白话16 小时前
前端必看!10个React实战技巧让你代码起飞,附超详细注释
前端·javascript·react.js