【Web】React 編寫全局組件

最常用的方式是 ContextProvider 的方式。但是,如果使用 ContextProvider 的方式,會在根部引入多個 .Provider,特別是當項目有多個全局狀態需要管理時,這可能會導致 Provider 嵌套過多,增加代碼復雜度和可維護性。

其他的替代方式

除了使用 Context + Provider 的方式管理全局狀態,還有其他幾種方法可以在不增加過多嵌套的情況下管理全局 Loading 狀態。

1. 通過全局狀態管理庫(如 Redux 或 Zustand)

使用像 Redux、Zustand 這樣的全局狀態管理庫可以在不增加過多嵌套的情況下管理全局狀態。

使用 Zustand 示例

Zustand 是一個輕量的狀態管理庫,比 Redux 更加簡潔,且無需在根部設置多個 Provider

// store/loadingStore.ts
import create from 'zustand';

interface LoadingState {
  isLoading: boolean;
  setLoading: (value: boolean) => void;
}

export const useLoadingStore = create<LoadingState>((set) => ({
  isLoading: false,
  setLoading: (value) => set({ isLoading: value }),
}));

在組件中使用:

// components/ExampleComponent.tsx
import { useEffect } from 'react';
import { useLoadingStore } from '../store/loadingStore';
import Loading from './Loading';

const ExampleComponent = () => {
  const { isLoading, setLoading } = useLoadingStore();

  useEffect(() => {
    setLoading(true);
    // 模擬異步操作
    setTimeout(() => {
      setLoading(false);
    }, 2000);
  }, [setLoading]);

  return (
    <div>
      {isLoading && <Loading />}
      <h1>This is an example component</h1>
    </div>
  );
};

export default ExampleComponent;

這樣就不需要 ProviderZustand 會自動處理狀態的共享和更新。

2. 使用 React Hooks 和單例模式

可以創建一個全局的單例模式的管理類,結合自定義的 React Hooks 來觸發全局的 Loading 狀態,而無需在根組件設置任何 Provider

示例代碼
// utils/loadingManager.ts
class LoadingManager {
  private static instance: LoadingManager;
  private subscribers: ((isLoading: boolean) => void)[] = [];

  private constructor() {}

  public static getInstance() {
    if (!LoadingManager.instance) {
      LoadingManager.instance = new LoadingManager();
    }
    return LoadingManager.instance;
  }

  public subscribe(callback: (isLoading: boolean) => void) {
    this.subscribers.push(callback);
  }

  public unsubscribe(callback: (isLoading: boolean) => void) {
    this.subscribers = this.subscribers.filter(sub => sub !== callback);
  }

  public setLoading(isLoading: boolean) {
    this.subscribers.forEach(callback => callback(isLoading));
  }
}

export default LoadingManager.getInstance();

在組件中使用:

// hooks/useLoading.ts
import { useEffect, useState } from 'react';
import LoadingManager from '../utils/loadingManager';

export const useLoading = () => {
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const handleLoadingChange = (loading: boolean) => {
      setIsLoading(loading);
    };

    LoadingManager.subscribe(handleLoadingChange);

    return () => {
      LoadingManager.unsubscribe(handleLoadingChange);
    };
  }, []);

  return { isLoading };
};

在具體業務組件中使用:

// components/ExampleComponent.tsx
import { useEffect } from 'react';
import LoadingManager from '../utils/loadingManager';
import { useLoading } from '../hooks/useLoading';
import Loading from './Loading';

const ExampleComponent = () => {
  const { isLoading } = useLoading();

  useEffect(() => {
    LoadingManager.setLoading(true);

    // 模擬異步操作
    setTimeout(() => {
      LoadingManager.setLoading(false);
    }, 2000);
  }, []);

  return (
    <div>
      {isLoading && <Loading />}
      <h1>This is an example component</h1>
    </div>
  );
};

export default ExampleComponent;

這樣你就不需要依賴 Provider,并且能夠在任何地方控制全局的 Loading 狀態。

3. 使用 React 的 Portal 和全局 DOM 元素

另一種方式是通過 React 的 Portal 渲染 Loading 組件到 DOM 的全局位置,例如在 body 中插入一個根 div,這樣也能實現全局加載組件的顯示。

示例代碼
// components/LoadingPortal.tsx
import { FC } from 'react';
import ReactDOM from 'react-dom';

const LoadingPortal: FC = () => {
  return ReactDOM.createPortal(
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900 bg-opacity-50">
      <div className="animate-spin rounded-full h-24 w-24 border-t-4 border-green-500"></div>
    </div>,
    document.body // 將 Loading 渲染到 body 中
  );
};

export default LoadingPortal;

在具體組件中使用:

// components/ExampleComponent.tsx
import { useState, useEffect } from 'react';
import LoadingPortal from './LoadingPortal';

const ExampleComponent = () => {
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    setIsLoading(true);
    // 模擬異步操作
    setTimeout(() => {
      setIsLoading(false);
    }, 2000);
  }, []);

  return (
    <div>
      {isLoading && <LoadingPortal />}
      <h1>This is an example component</h1>
    </div>
  );
};

export default ExampleComponent;

這種方式也不需要 Provider,并且可以將全局加載組件通過 Portal 渲染到 DOM 的任何地方。

總結

  • 使用 ZustandRedux 可以通過狀態管理庫來管理全局 Loading 狀態,避免過多的 Provider 嵌套。
  • 通過 單例模式 + 自定義 hooks 可以實現靈活的全局狀態控制,不依賴 Provider
  • 通過 React Portal,可以將 Loading 渲染到全局 DOM 節點,簡單易用。

這些方法都可以根據具體的項目需求來選擇,能夠避免多層級 Provider 嵌套問題,保持代碼結構簡潔明了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容