亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

React實(shí)現(xiàn)浮層組件的思路與方法詳解

 更新時(shí)間:2024年02月06日 15:42:35   作者:lecepin  
React?浮層組件(也稱(chēng)為彈出組件或彈窗組件)通常是指在用戶(hù)界面上浮動(dòng)顯示的組件,本文主要介紹了浮層組件的實(shí)現(xiàn)方法,感興趣的小伙伴可以了解下

React 浮層組件(也稱(chēng)為彈出組件或彈窗組件)通常是指在用戶(hù)界面上浮動(dòng)顯示的組件,它們脫離常規(guī)的文檔流,并且可以在用戶(hù)進(jìn)行某些操作時(shí)出現(xiàn)在頁(yè)面的最上層。React 浮層組件可以用于創(chuàng)建模態(tài)框(Modal)、下拉菜單(Dropdown)、工具提示(Tooltip)、側(cè)邊欄(Sidebar)或任何其他需要?jiǎng)討B(tài)顯示和隱藏且通常位置固定或絕對(duì)定位的內(nèi)容。

React 浮層組件的特點(diǎn)包括:

  • 層級(jí)管理:浮層組件通常具有較高的z-index值,使得它們能夠顯示在其他內(nèi)容之上。
  • 動(dòng)態(tài)性:它們通常是響應(yīng)用戶(hù)交互(如點(diǎn)擊按鈕或鼠標(biāo)懸停)而顯示的,并且可以通過(guò)用戶(hù)交互(如點(diǎn)擊遮罩層或按下 Escape 鍵)來(lái)關(guān)閉。
  • 定位:浮層組件可以根據(jù)觸發(fā)它們的元素(如按鈕或鏈接)的位置動(dòng)態(tài)定位。
  • 獨(dú)立渲染:為了避免 CSS 布局的限制(如overflow或父元素的z-index),浮層組件通常使用 React 的ReactDOM.createPortal方法渲染到 DOM 樹(shù)的其他位置,比如直接渲染到document.body下。
  • 可訪問(wèn)性(Accessibility):良好的浮層組件應(yīng)該考慮到可訪問(wèn)性,比如焦點(diǎn)管理、鍵盤(pán)導(dǎo)航和屏幕閱讀器的支持。

1. 實(shí)現(xiàn)簡(jiǎn)單的 Tootip

Tootip 就是簡(jiǎn)單的文字提示氣泡框。

簡(jiǎn)單實(shí)現(xiàn):

import React, { useState, useRef } from "react";

const Tooltip = ({ children, text }) => {
  const [visible, setVisible] = useState(false);
  const tooltipRef = useRef();

  const showTooltip = () => setVisible(true);
  const hideTooltip = () => setVisible(false);

  // 獲取觸發(fā)元素的位置,以便正確定位Tooltip
  const computePosition = () => {
    if (tooltipRef.current) {
      const { top, height } = tooltipRef.current.getBoundingClientRect();
      return {
        top: top + height + window.scrollY,
        left: 0 + window.scrollX,
      };
    }

    return { top: 0, left: 0 };
  };

  const tooltipStyle = {
    position: "absolute",
    border: "1px solid #ccc",
    backgroundColor: "white",
    padding: "5px",
    zIndex: 1000,
    ...computePosition(), // 計(jì)算位置并應(yīng)用樣式
    display: visible ? "block" : "none", // 控制顯示與隱藏
  };

  return (
    <div
      style={{ position: "relative", display: "inline-block" }}
      ref={tooltipRef}
    >
      <div
        style={tooltipStyle}
        onMouseEnter={showTooltip}
        onMouseLeave={hideTooltip}
      >
        {text}
      </div>
      <div onMouseEnter={showTooltip} onMouseLeave={hideTooltip}>
        {children}
      </div>
    </div>
  );
};

// 使用Tooltip的組件
const App = () => {
  return (
    <div>
      <p>
        Hover over the word{" "}
        <Tooltip text="A helpful tooltip.">"tooltip"</Tooltip> to see the
        tooltip.
      </p>
    </div>
  );
};

2. 存在問(wèn)題

上面的 Tooltip 實(shí)現(xiàn)確實(shí)可能帶來(lái)一些問(wèn)題。以下是一些主要考慮點(diǎn):

影響原布局

  • 由于 Tooltip 采用絕對(duì)定位,它可能會(huì)覆蓋頁(yè)面上的其他元素而不是占據(jù)自己的空間。這可能導(dǎo)致頁(yè)面元素的重疊,尤其是當(dāng) Tooltip 很大的時(shí)候。
  • 如果 Tooltip 的觸發(fā)元素沒(méi)有設(shè)置合適的定位上下文(如 position: relative),Tooltip 可能會(huì)相對(duì)于錯(cuò)誤的父元素定位,出現(xiàn)在頁(yè)面意外的位置。

可能被隱藏

  • 如果 Tooltip 的父元素設(shè)置了 overflow: hidden 或者 clip-path 屬性,這可能會(huì)導(dǎo)致 Tooltip 被裁剪或完全隱藏。
  • 當(dāng) Tooltip 出現(xiàn)在視口邊緣附近時(shí),部分 Tooltip 可能會(huì)位于視口外,從而導(dǎo)致內(nèi)容不完全可見(jiàn)。

性能問(wèn)題

  • Tooltip 的位置計(jì)算涉及 DOM 的讀取操作(如 getBoundingClientRect),這可能會(huì)導(dǎo)致回流(reflow)和重繪(repaint),尤其是在大型應(yīng)用中或在頻繁更新的情況下。
  • 鼠標(biāo)事件的頻繁觸發(fā)(如 onMouseEnter 和 onMouseLeave)可能導(dǎo)致 Tooltip 的頻繁顯示和隱藏,進(jìn)而引發(fā)性能問(wèn)題。

可訪問(wèn)性問(wèn)題

  • 不使用 createPortal 的 Tooltip 可能不會(huì)管理焦點(diǎn),這會(huì)影響鍵盤(pán)用戶(hù)和屏幕閱讀器用戶(hù)的體驗(yàn)。
  • 如果 Tooltip 中的內(nèi)容對(duì)用戶(hù)理解頁(yè)面很重要,但僅通過(guò)鼠標(biāo)懸停來(lái)顯示,鍵盤(pán)用戶(hù)可能無(wú)法訪問(wèn)這些信息。

屏幕適配問(wèn)題

  • Tooltip 在移動(dòng)設(shè)備上可能表現(xiàn)不佳,因?yàn)橐苿?dòng)設(shè)備通常沒(méi)有懸停狀態(tài),可能需要其他機(jī)制來(lái)觸發(fā) Tooltip。
  • 在響應(yīng)式設(shè)計(jì)中,Tooltip 可能需要不同的樣式或定位策略以適應(yīng)不同屏幕尺寸。

復(fù)雜度增加

當(dāng)頁(yè)面中有許多 Tooltip 時(shí),管理它們的位置和可見(jiàn)性狀態(tài)可能會(huì)變得復(fù)雜。

為了解決這些問(wèn)題,您可能需要進(jìn)一步優(yōu)化 Tooltip 組件,例如:

  • 使用 debounce 或 throttle 函數(shù)來(lái)減少位置計(jì)算的頻率,優(yōu)化性能。
  • 添加額外的邏輯來(lái)確保 Tooltip 在視口內(nèi)完全可見(jiàn),例如通過(guò)調(diào)整位置或改變 Tooltip 出現(xiàn)的方向。
  • 通過(guò)添加適合鍵盤(pán)導(dǎo)航的交互來(lái)提高可訪問(wèn)性,例如使 Tooltip 在獲取焦點(diǎn)時(shí)顯示。
  • 為移動(dòng)設(shè)備實(shí)現(xiàn)不同的交互模式,或者完全避免在小屏幕上使用 Tooltip。

盡管有這些潛在的問(wèn)題,但在某些情況下,這種不使用 createPortal 的實(shí)現(xiàn)方式可能足夠滿足簡(jiǎn)單的需求。對(duì)于更復(fù)雜的情況,則可能需要考慮更高級(jí)的解決方案,例如使用 React.createPortal 或第三方庫(kù),這些庫(kù)已經(jīng)解決了上述問(wèn)題。

3. createPortal

ReactDOM.createPortal 是 React 提供的一個(gè) API,它允許你把子節(jié)點(diǎn)渲染到存在于父組件之外的 DOM 節(jié)點(diǎn)上。這個(gè)方法的簽名如下:

ReactDOM.createPortal(child, container);

其中 child 是任何可以渲染的 React 子元素,例如一個(gè)元素、字符串或碎片(fragment),而 container 是一個(gè) DOM 元素。

createPortal 的主要用處包括:

事件冒泡:使用 createPortal 渲染的子組件會(huì)在 DOM 樹(shù)上處于不同的位置,但從 React 的角度來(lái)看,它仍然存在于 React 組件樹(shù)中原來(lái)的位置,這意味著事件可以正常冒泡到 React 父組件,盡管這些元素在 DOM 層級(jí)結(jié)構(gòu)中并不直接相連。

避免 CSS 約束:在某些情況下,你可能不希望子組件受到父組件 CSS 的影響,例如在一個(gè)設(shè)置了overflow: hiddenz-index的父元素內(nèi)部渲染一個(gè)模態(tài)對(duì)話框(modal)或工具提示(tooltip)。通過(guò)使用 createPortal,模態(tài)或工具提示可以渲染到 DOM 樹(shù)中的其他位置,例如直接渲染到document.body下,從而避免了這些 CSS 約束。

視覺(jué)上的“脫離”:當(dāng)你需要組件在視覺(jué)上位于頁(yè)面層次結(jié)構(gòu)之外時(shí)(如模態(tài)框、通知、懸浮卡片等),createPortal 提供了一種將組件結(jié)構(gòu)上與視覺(jué)結(jié)構(gòu)分離的方法。這可能是因?yàn)檫@些組件需要在頁(yè)面上占據(jù)最頂層,以避免被其他元素遮擋。

createPortal 注意事項(xiàng)

假設(shè)你指的是 "reactportal" 中可能遇到的問(wèn)題,那么在使用 React 的 createPortal API 時(shí),以下是一些可能遇到的典型問(wèn)題和挑戰(zhàn):

  • 事件冒泡:使用 createPortal 創(chuàng)建的 React 元素雖然在 DOM 樹(shù)中是在不同位置,但它們?cè)?React 組件樹(shù)中仍然保持原位置。因此,事件冒泡將不按照 DOM 結(jié)構(gòu)進(jìn)行,而是沿著 React 組件樹(shù)向上冒泡。這可能導(dǎo)致一些意料之外的行為,尤其是在復(fù)雜的事件處理邏輯中。
  • 樣式隔離:當(dāng)彈出的內(nèi)容移動(dòng)到 DOM 樹(shù)的其他部分時(shí),可能會(huì)丟失某些由上層組件提供的 CSS 樣式。你可能需要額外處理以確保彈出內(nèi)容的樣式與預(yù)期一致。
  • 上下文不一致:雖然 createPortal 允許在 React 組件樹(shù)中保留上下文,但如果你在 DOM 樹(shù)中的其他位置使用 Portal,與 Portal 交互的 DOM 元素可能不會(huì)有相同的上下文(例如,React 的 Context API)。
  • 輔助技術(shù)的支持:移動(dòng)到 DOM 樹(shù)其他位置的內(nèi)容可能會(huì)影響輔助技術(shù)(如屏幕閱讀器)對(duì)頁(yè)面的解讀,因?yàn)檫@些內(nèi)容在語(yǔ)義結(jié)構(gòu)上可能被解讀為與它們?cè)谄聊簧系囊曈X(jué)位置不一致。
  • 性能考量:如果你頻繁創(chuàng)建和銷(xiāo)毀 Portal,或者 Portal 中包含大量動(dòng)態(tài)內(nèi)容,這可能會(huì)影響應(yīng)用的性能。
  • 服務(wù)端渲染(SSR)兼容性:在服務(wù)端渲染環(huán)境下,由于沒(méi)有 document.body,使用 createPortal 可能需要額外的處理來(lái)確保代碼的兼容性。
  • 滾動(dòng)和定位問(wèn)題:如果 Portal 內(nèi)容需要基于觸發(fā)元素的位置進(jìn)行定位,頁(yè)面滾動(dòng)或窗口大小變化時(shí)可能需要更新位置,這可能會(huì)需要額外的事件監(jiān)聽(tīng)和狀態(tài)管理。
  • 嵌套 Portal:在 Portal 內(nèi)部創(chuàng)建另一個(gè) Portal 可能會(huì)導(dǎo)致一些復(fù)雜的層級(jí)問(wèn)題,特別是在處理事件和樣式時(shí)。
  • 可訪問(wèn)性(a11y):創(chuàng)建的 Portal 內(nèi)容可能會(huì)打亂鍵盤(pán)導(dǎo)航順序,或者改變焦點(diǎn)管理的預(yù)期行為。你可能需要手動(dòng)管理焦點(diǎn)以確保良好的可訪問(wèn)性。

為了解決這些問(wèn)題,你可能需要實(shí)施一些策略,比如在樣式上使用更高的特異性,使用輔助技術(shù)友好的方法來(lái)管理焦點(diǎn),或者確保上下文在 Portal 內(nèi)部和外部保持一致。此外,對(duì)于性能和兼容性問(wèn)題,可能需要一些額外的優(yōu)化和測(cè)試。

4. cloneElement

React 的 cloneElement 函數(shù)允許你克隆一個(gè) React 元素,并傳入新的 props、ref。這個(gè)函數(shù)的簽名如下:

React.cloneElement(element, [props], [...children]);
  • element 是你想要克隆的 React 元素。
  • props 是一個(gè)對(duì)象,其中包含了你希望在克隆的元素上設(shè)置或覆蓋的新屬性。
  • children 是任意數(shù)量的子元素,用于替換克隆元素的子元素。

cloneElement 主要用于以下幾種場(chǎng)景:

  • 屬性增強(qiáng):當(dāng)你需要增強(qiáng)一個(gè)元素的功能而又不想顯式創(chuàng)建一個(gè)新的組件時(shí),你可以使用 cloneElement 來(lái)添加或修改屬性。例如,你可以為一個(gè)元素添加額外的 onMouseEnter、onClick 等事件處理器。
  • 條件渲染:你可以對(duì)子組件進(jìn)行有條件的修改,例如在特定情況下為子組件添加附加的 props 或樣式。
  • 引用保持cloneElement 在克隆時(shí)保持子元素類(lèi)型的不變和 key 的不變,這有助于保持組件的狀態(tài)和避免不必要的卸載和重新掛載。
  • 與高階組件(Higher-Order Components, HOCs)結(jié)合:在開(kāi)發(fā)高階組件時(shí),cloneElement 可以用來(lái)包裹傳入的子組件,并為其注入需要的 props。
  • 與 React 的 Context 結(jié)合:有時(shí)候你可能需要在組件樹(shù)中深處的組件接收到來(lái)自頂層的 props,為了避免明確地傳遞這些 props,你可以使用 cloneElement 結(jié)合 Context API 來(lái)靈活地為深層組件注入所需的數(shù)據(jù)。

以下是一個(gè)使用 cloneElement 來(lái)增強(qiáng)子組件 onMouseEnter 事件的例子:

import React, { cloneElement } from "react";

class EnhancedComponent extends React.Component {
  handleMouseEnter = () => {
    // 提供額外的 onMouseEnter 行為
    console.log("Mouse entered!");
  };

  render() {
    const { children } = this.props;

    // 假設(shè)我們只處理一個(gè)子元素的情況
    const child = React.Children.only(children);

    // 克隆子元素并注入新的 onMouseEnter 處理器
    const enhancedChild = cloneElement(child, {
      onMouseEnter: this.handleMouseEnter,
    });

    return enhancedChild;
  }
}

export default EnhancedComponent;

在這個(gè)例子中,EnhancedComponent 接收一個(gè)單一的子組件,并使用 cloneElement 對(duì)其進(jìn)行克隆,添加一個(gè)新的 onMouseEnter 事件處理器。如果原始子組件已經(jīng)有 onMouseEnter 處理器,這個(gè)新的處理器會(huì)被合并,兩個(gè)處理器都會(huì)被執(zhí)行。

總之,cloneElement 在不同的用例中都很有用,尤其是當(dāng)你想要微調(diào)元素的屬性或行為而又不想創(chuàng)建全新的組件時(shí)。然而,過(guò)度使用 cloneElement 可能使組件變得難以理解和維護(hù),因此應(yīng)該謹(jǐn)慎使用。

5. 重寫(xiě) Tootip

使用 cloneElement 和 createPortal 來(lái)實(shí)現(xiàn)一個(gè) Tooltip 組件,可以將 Tooltip 的內(nèi)容渲染到頁(yè)面的頂級(jí)位置,同時(shí)注入事件處理器到觸發(fā)元素。這樣的實(shí)現(xiàn)可以解決上文提到的一些問(wèn)題,例如避免因?yàn)?nbsp;overflow 或 z-index 造成的渲染問(wèn)題。

以下是一個(gè)簡(jiǎn)單的函數(shù)式組件模式的 Tooltip 實(shí)現(xiàn):

import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";

const Tooltip = ({ children, content }) => {
  const [show, setShow] = useState(false);
  const [position, setPosition] = useState({ top: 0, left: 0 });
  const childRef = useRef(null);

  const handleMouseEnter = () => {
    if (childRef.current) {
      const rect = childRef.current.getBoundingClientRect();
      setPosition({
        top: rect.bottom + window.scrollY,
        left: rect.left + window.scrollX,
      });
    }
    setShow(true);
  };

  const handleMouseLeave = () => {
    setShow(false);
  };

  useEffect(() => {
    window.addEventListener("scroll", handleMouseLeave);
    return () => {
      window.removeEventListener("scroll", handleMouseLeave);
    };
  }, []);

  const tooltip =
    show &&
    ReactDOM.createPortal(
      <div
        style={{
          position: "absolute",
          top: position.top,
          left: position.left,
          zIndex: 1000,
          backgroundColor: "#fff",
          border: "1px solid #ddd",
          padding: "5px",
          borderRadius: "3px",
          boxShadow: "0 2px 5px rgba(0,0,0,0.2)",
        }}
      >
        {content}
      </div>,
      document.body
    );

  const clonedChild = React.cloneElement(children, {
    onMouseEnter: handleMouseEnter,
    onMouseLeave: handleMouseLeave,
    ref: childRef,
  });

  return (
    <>
      {clonedChild}
      {tooltip}
    </>
  );
};

// 使用Tooltip的組件
const App = () => {
  return (
    <div style={{ marginTop: "100px", marginLeft: "100px" }}>
      <Tooltip content="This is a tooltip!">
        <button>Hover over me!</button>
      </Tooltip>
    </div>
  );
};

export default App;

在這個(gè)例子中,Tooltip 組件接受一個(gè) children 屬性和一個(gè) content 屬性。children 是觸發(fā) Tooltip 的元素,content 是顯示在 Tooltip 中的內(nèi)容。

使用 cloneElement,我們?yōu)?nbsp;children 元素克隆一個(gè)新版本并添加了 onMouseEnter 和 onMouseLeave 事件處理器,用于控制 Tooltip 的顯示和隱藏。

我們使用 createPortal 將 Tooltip 內(nèi)容渲染到 document.body 中,這樣 Tooltip 就能夠避開(kāi)任何本地 CSS 的限制。show 狀態(tài)控制 Tooltip 的顯示,position 狀態(tài)用于計(jì)算 Tooltip 應(yīng)該出現(xiàn)在頁(yè)面上的位置。

通過(guò) useEffect,我們添加了對(duì) scroll 事件的監(jiān)聽(tīng)來(lái)在頁(yè)面滾動(dòng)時(shí)隱藏 Tooltip,防止 Tooltip 位置不正確。

這樣,你就得到了一個(gè)使用 cloneElement 和 createPortal 實(shí)現(xiàn)的 Tooltip 組件,它可以在不影響頁(yè)面布局和樣式的情況下工作,并且能夠在頁(yè)面上的任何位置正確地顯示 Tooltip。

5.1 同步元素滾動(dòng)

上面的代碼示例中,Tooltip 在觸發(fā)元素的 onMouseEnter 事件處理器中計(jì)算其顯示位置,并在 onMouseLeave 或窗口的 scroll 事件中被隱藏。這意味著一旦 Tooltip 顯示出來(lái),如果用戶(hù)滾動(dòng)頁(yè)面,Tooltip 會(huì)保持在首次顯示時(shí)計(jì)算出的固定位置,而不會(huì)跟隨觸發(fā)元素移動(dòng)。

如果要實(shí)現(xiàn) Tooltip 位置與觸發(fā)元素同步滾動(dòng)的效果,需要?jiǎng)討B(tài)更新 Tooltip 的位置,以響應(yīng)頁(yè)面的滾動(dòng)事件。這可以通過(guò)在 useEffect 鉤子中添加對(duì)滾動(dòng)事件的監(jiān)聽(tīng)來(lái)實(shí)現(xiàn)。在滾動(dòng)事件的回調(diào)中,我們可以重新計(jì)算 Tooltip 的位置,使其與觸發(fā)元素保持同步。

以下是更新后的代碼示例,實(shí)現(xiàn)了 Tooltip 與觸發(fā)元素同步滾動(dòng)的效果:

import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";

const Tooltip = ({ children, content }) => {
  const [show, setShow] = useState(false);
  const childRef = useRef(null);

  const updatePosition = () => {
    if (childRef.current) {
      const rect = childRef.current.getBoundingClientRect();
      return {
        top: rect.bottom + window.scrollY,
        left: rect.left + window.scrollX,
      };
    }
  };

  const [position, setPosition] = useState({ top: 0, left: 0 });

  const handleMouseEnter = () => {
    setPosition(updatePosition());
    setShow(true);
  };

  const handleMouseLeave = () => {
    setShow(false);
  };

  useEffect(() => {
    const handleScroll = () => {
      if (show) {
        setPosition(updatePosition());
      }
    };

    window.addEventListener("scroll", handleScroll);

    // 清理函數(shù)
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [show]); // 依賴(lài)于 `show`,僅當(dāng) Tooltip 顯示時(shí)添加事件監(jiān)聽(tīng)

  const tooltip =
    show &&
    ReactDOM.createPortal(
      <div
        style={{
          position: "absolute",
          top: position.top,
          left: position.left,
          zIndex: 1000,
          backgroundColor: "#fff",
          border: "1px solid #ddd",
          padding: "5px",
          borderRadius: "3px",
          boxShadow: "0 2px 5px rgba(0,0,0,0.2)",
          // 添加 transition 效果使位置更新更平滑
          transition: "top 0.3s, left 0.3s",
        }}
      >
        {content}
      </div>,
      document.body
    );

  const clonedChild = React.cloneElement(children, {
    onMouseEnter: handleMouseEnter,
    onMouseLeave: handleMouseLeave,
    ref: childRef,
  });

  return (
    <>
      {clonedChild}
      {tooltip}
    </>
  );
};

// 使用Tooltip的組件
const App = () => {
  return (
    <div style={{ marginTop: "100px", marginLeft: "100px" }}>
      <Tooltip content="This is a tooltip!">
        <button>Hover over me!</button>
      </Tooltip>
    </div>
  );
};

export default App;

在這個(gè)更新的實(shí)現(xiàn)中,添加了 updatePosition 函數(shù),它負(fù)責(zé)根據(jù)當(dāng)前觸發(fā)元素的位置來(lái)更新 Tooltip 的位置。在 useEffect 中注冊(cè)了頁(yè)面滾動(dòng)的事件監(jiān)聽(tīng)器 handleScroll,它會(huì)在頁(yè)面滾動(dòng)時(shí)調(diào)用 updatePosition 來(lái)更新 Tooltip 的位置。監(jiān)聽(tīng)器僅在 Tooltip 顯示時(shí)進(jìn)行注冊(cè),從而避免不必要的事件監(jiān)聽(tīng)和執(zhí)行。

綜上所述,更新后的代碼使得 Tooltip 能夠在頁(yè)面滾動(dòng)時(shí)跟隨觸發(fā)元素移動(dòng),從而解決了位置同步的問(wèn)題。

5.2 防止 cloneElement 注入破壞

在使用 cloneElement 對(duì)子組件增強(qiáng)或注入新的屬性和事件處理器時(shí),需要特別注意不要覆蓋子組件原有的屬性和事件處理器。為了避免這種覆蓋,可將原有的屬性和處理器與新的屬性和處理器合并。

下面是一個(gè)示例,它展示了如何使用 cloneElement 來(lái)增強(qiáng)子組件的 onMouseEnter 和 onMouseLeave 事件處理器,同時(shí)保留原有的事件處理器:

import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";

const Tooltip = ({ children, content }) => {
  const [show, setShow] = useState(false);
  const childRef = useRef(null);

  const handleMouseEnter = (originalOnMouseEnter) => (event) => {
    // 如果子組件有自己的 onMouseEnter 事件處理器,先調(diào)用它
    if (originalOnMouseEnter) {
      originalOnMouseEnter(event);
    }
    // 然后執(zhí)行 Tooltip 特定的邏輯
    setShow(true);
  };

  const handleMouseLeave = (originalOnMouseLeave) => (event) => {
    // 如果子組件有自己的 onMouseLeave 事件處理器,先調(diào)用它
    if (originalOnMouseLeave) {
      originalOnMouseLeave(event);
    }
    // 然后執(zhí)行 Tooltip 特定的邏輯
    setShow(false);
  };

  const tooltipElement =
    show &&
    ReactDOM.createPortal(
      <div
        style={
          {
            /* Tooltip樣式 */
          }
        }
      >
        {content}
      </div>,
      document.body
    );

  // 克隆子組件,合并事件處理器
  const clonedChild = React.cloneElement(children, {
    ref: childRef,
    onMouseEnter: handleMouseEnter(children.props.onMouseEnter),
    onMouseLeave: handleMouseLeave(children.props.onMouseLeave),
  });

  return (
    <>
      {clonedChild}
      {tooltipElement}
    </>
  );
};

// 使用Tooltip的組件
const App = () => {
  const handleButtonMouseEnter = () => {
    console.log("Button's original onMouseEnter called");
  };

  const handleButtonMouseLeave = () => {
    console.log("Button's original onMouseLeave called");
  };

  return (
    <div style={{ marginTop: "100px", marginLeft: "100px" }}>
      <Tooltip content="This is a tooltip!">
        <button
          onMouseEnter={handleButtonMouseEnter}
          onMouseLeave={handleButtonMouseLeave}
        >
          Hover over me!
        </button>
      </Tooltip>
    </div>
  );
};

export default App;

在 Tooltip 組件中,我們?yōu)?nbsp;handleMouseEnter 和 handleMouseLeave 方法分別傳入了子組件原有的 onMouseEnter 和 onMouseLeave 事件處理器。然后在這些方法的閉包中,如果存在原有的事件處理器,我們先調(diào)用這些原有的事件處理器,接著執(zhí)行 Tooltip 的邏輯。這樣一來(lái),我們就可以確保子組件的原有行為不會(huì)被 Tooltip 組件的行為覆蓋。

如此,cloneElement 能夠安全地用于增強(qiáng)子組件,而不會(huì)破壞子組件的預(yù)期行為。

6. 其他優(yōu)化

在實(shí)現(xiàn) React 組件時(shí),尤其是在開(kāi)發(fā)像 Tooltip 這樣可能大量使用的組件時(shí),考慮性能優(yōu)化是非常重要的。下面是一些性能優(yōu)化的建議:

避免不必要的重新渲染

  • 使用 React.memo 來(lái)包裹函數(shù)組件,避免在 props 沒(méi)有改變的情況下發(fā)生不必要的渲染。
  • 確保你的組件盡可能地只在必要時(shí)更新。比如,如果 Tooltip 組件的狀態(tài)沒(méi)有改變,就沒(méi)有必要更新 DOM。

減少重計(jì)算

  • 通過(guò)緩存計(jì)算結(jié)果(如使用 useMemo 鉤子),減少 Tooltip 位置計(jì)算的次數(shù)。
  • 使用 throttle 或 debounce 函數(shù)限制事件處理器的調(diào)用頻率,尤其是對(duì)于像 resize 和 scroll 這樣可能頻繁觸發(fā)的事件。

優(yōu)化事件處理器

  • 確保添加的事件監(jiān)聽(tīng)器(比如滾動(dòng)監(jiān)聽(tīng)器)在組件卸載時(shí)被移除,以避免內(nèi)存泄漏。
  • 如果有可能,使用事件委托來(lái)減少事件監(jiān)聽(tīng)器的數(shù)量。

使用 shouldComponentUpdate 或 React.PureComponent:

  • 對(duì)于類(lèi)組件,可以通過(guò)實(shí)現(xiàn) shouldComponentUpdate 方法來(lái)避免不必要的更新。
  • 如果你的類(lèi)組件擁有不可變的 props 和 state,可以考慮使用 React.PureComponent,它自動(dòng)為 shouldComponentUpdate 提供了一個(gè)淺比較實(shí)現(xiàn)。

減少 DOM 操作

對(duì)于使用 createPortal 的組件,盡可能減少 DOM 節(jié)點(diǎn)的插入和移除操作??梢钥紤]在應(yīng)用的頂層預(yù)先定義好掛載點(diǎn),而不是動(dòng)態(tài)創(chuàng)建和銷(xiāo)毀。

利用 CSS 動(dòng)畫(huà)代替 JS 動(dòng)畫(huà)

當(dāng)可能時(shí),使用 CSS 動(dòng)畫(huà)和過(guò)渡效果,因?yàn)樗鼈兛梢岳?GPU 加速,而 JS 動(dòng)畫(huà)可能會(huì)觸發(fā)更多的重繪和回流。

使用懶加載

如果 Tooltip 內(nèi)容很大或包含圖片等資源,可以考慮使用懶加載技術(shù),只有當(dāng) Tooltip 顯示時(shí)才加載內(nèi)容。

避免內(nèi)聯(lián)函數(shù)定義

避免在渲染方法中定義內(nèi)聯(lián)函數(shù),因?yàn)檫@將在每次渲染時(shí)創(chuàng)建新的函數(shù)實(shí)例,這可能會(huì)導(dǎo)致子組件的不必要重渲染。

分離組件

將大型組件拆分成更小、更容易管理的子組件,這樣可以更精細(xì)地控制渲染行為。

使用 key 屬性

當(dāng)渲染列表或集合時(shí),確保每個(gè)元素都有一個(gè)獨(dú)特的 key 屬性,這可以幫助 React 在更新過(guò)程中識(shí)別和重用 DOM 節(jié)點(diǎn)。

通過(guò)實(shí)施上述優(yōu)化策略,你可以提高組件的性能,特別是在渲染大量 Tooltip 或在滾動(dòng)等高頻事件觸發(fā)時(shí)。性能優(yōu)化是一個(gè)持續(xù)的過(guò)程,始終需要根據(jù)實(shí)際場(chǎng)景和應(yīng)用需求來(lái)評(píng)估和調(diào)整。

以上就是React實(shí)現(xiàn)浮層組件的思路與方法詳解的詳細(xì)內(nèi)容,更多關(guān)于React浮層組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React項(xiàng)目配置prettier和eslint的方法

    React項(xiàng)目配置prettier和eslint的方法

    這篇文章主要介紹了React項(xiàng)目配置prettier和eslint的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • react配置webpack-bundle-analyzer項(xiàng)目?jī)?yōu)化踩坑記錄

    react配置webpack-bundle-analyzer項(xiàng)目?jī)?yōu)化踩坑記錄

    這篇文章主要介紹了react配置webpack-bundle-analyzer項(xiàng)目?jī)?yōu)化踩坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • React中state屬性案例詳解

    React中state屬性案例詳解

    在React中,state 是一個(gè)用于存儲(chǔ)組件內(nèi)部數(shù)據(jù)的特殊對(duì)象,每個(gè)React組件都可以包含自己的state,我們往往是通過(guò)修改state的值來(lái)驅(qū)動(dòng)React重新渲染組件,這篇文章主要介紹了React中state屬性,需要的朋友可以參考下
    2023-11-11
  • 詳解關(guān)于react-redux中的connect用法介紹及原理解析

    詳解關(guān)于react-redux中的connect用法介紹及原理解析

    本篇文章主要介紹了詳解關(guān)于react-redux中的connect用法介紹及原理解析,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-09-09
  • React?中?memo?useMemo?useCallback?到底該怎么用

    React?中?memo?useMemo?useCallback?到底該怎么用

    在React函數(shù)組件中,當(dāng)組件中的props發(fā)生變化時(shí),默認(rèn)情況下整個(gè)組件都會(huì)重新渲染。換句話說(shuō),如果組件中的任何值更新,整個(gè)組件將重新渲染,包括沒(méi)有更改values/props的函數(shù)/組件。在react中,我們可以通過(guò)memo,useMemo以及useCallback來(lái)防止子組件的rerender
    2022-10-10
  • React跨端動(dòng)態(tài)化之從JS引擎到RN落地詳解

    React跨端動(dòng)態(tài)化之從JS引擎到RN落地詳解

    這篇文章主要為大家介紹了React跨端動(dòng)態(tài)化之從JS引擎到RN落地,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • React前端開(kāi)發(fā)createElement源碼解讀

    React前端開(kāi)發(fā)createElement源碼解讀

    這篇文章主要為大家介紹了React前端開(kāi)發(fā)createElement源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 淺談React?Refs?使用場(chǎng)景及核心要點(diǎn)

    淺談React?Refs?使用場(chǎng)景及核心要點(diǎn)

    本文主要介紹了React?Refs?使用場(chǎng)景及核心要點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • webpack4 + react 搭建多頁(yè)面應(yīng)用示例

    webpack4 + react 搭建多頁(yè)面應(yīng)用示例

    這篇文章主要介紹了webpack4 + react 搭建多頁(yè)面應(yīng)用示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • react父組件調(diào)用子組件的方式匯總

    react父組件調(diào)用子組件的方式匯總

    在react中常用props實(shí)現(xiàn)子組件數(shù)據(jù)到父組件的傳遞,但是父組件調(diào)用子組件的功能卻不常用,下面這篇文章主要給大家介紹了關(guān)于react父組件調(diào)用子組件的相關(guān)資料,需要的朋友可以參考下
    2022-08-08

最新評(píng)論