最常用的方式是 Context
和 Provider
的方式。但是,如果使用 Context
和 Provider
的方式,會在根部引入多個 .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;
這樣就不需要 Provider
,Zustand
會自動處理狀態的共享和更新。
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 的任何地方。
總結
- 使用 Zustand 或 Redux 可以通過狀態管理庫來管理全局
Loading
狀態,避免過多的Provider
嵌套。 - 通過 單例模式 + 自定義 hooks 可以實現靈活的全局狀態控制,不依賴
Provider
。 - 通過 React Portal,可以將
Loading
渲染到全局 DOM 節點,簡單易用。
這些方法都可以根據具體的項目需求來選擇,能夠避免多層級 Provider
嵌套問題,保持代碼結構簡潔明了。