【Web】React 编写全局组件
最常用的方式是 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
嵌套问题,保持代码结构简洁明了。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 时光·李记
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果