React 中实现全局 Loader

cccs7 Lv5

使用 Redux + 自定义 Hook

准备 Loader

Loader.tsx

1
2
3
4
5
6
7
8
9
10
import React from 'react';  
import styled from 'styled-components';

const Loader = () => {
return (
<StyledWrapper>
<span className="loader" />
</StyledWrapper>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import Loader from './loader';
import { selectLoading } from '@/store/features/loader/loaderSlice';

const GlobalLoader = () => {
// 从Redux状态中获取loading状态
const isLoading = useSelector(selectLoading);

// 如果不在加载状态,则不渲染任何内容
if (!isLoading) return null;

return (
<LoaderOverlay>
<Loader />
</LoaderOverlay>
);
};

const LoaderOverlay = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.1);
z-index: 9999; // 确保Loader显示在所有内容之上
`;

export default GlobalLoader;

use-loader.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { useDispatch } from 'react-redux';
import { showLoader, hideLoader } from '@/store/features/loader/loaderSlice';

/**
* 自定义Hook,用于控制全局加载状态
* @returns 包含显示和隐藏加载器的方法的对象
*/
export const useLoader = () => {
const dispatch = useDispatch();

// 显示全局加载器
const showGlobalLoader = () => {
dispatch(showLoader());
};

// 隐藏全局加载器
const hideGlobalLoader = () => {
dispatch(hideLoader());
};

return {
showGlobalLoader,
hideGlobalLoader,
};
};

全局 Loader

global-loader.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import Loader from './loader';
import { selectLoading } from '@/store/features/loader/loaderSlice';

const GlobalLoader = () => {
// 从Redux状态中获取loading状态
const isLoading = useSelector(selectLoading);

// 如果不在加载状态,则不渲染任何内容
if (!isLoading) return null;

return (
<LoaderOverlay>
<Loader />
</LoaderOverlay>
);
};

const LoaderOverlay = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.1);
z-index: 9999; // 确保Loader显示在所有内容之上
`;

export default GlobalLoader;

提供全局加载状态的UI展示

这个组件应该被放置在应用的根组件中,以确保加载状态可以在整个应用中显示

loader-provider.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import GlobalLoader from './global-loader';

/**
* LoaderProvider组件
* 提供全局加载状态的UI展示
* 这个组件应该被放置在应用的根组件中,以确保加载状态可以在整个应用中显示
*/
export const LoaderProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
return (
<>
<GlobalLoader />
{children}
</>
);
};

根组件加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default function Providers({children}: { children: React.ReactNode }) {
return (
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<Provider store={store}>
<LoaderProvider>
<App/>
{children}
<RouterProvider router={router}/>
<Toaster/>
</LoaderProvider>
</Provider>
</ThemeProvider>
);
}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import React from 'react';
import { Button } from '@/components/ui/button';
import { useLoader } from '@/hooks/use-loader';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import Loader from "@/components/loader/loader.tsx";

/**
* Loader示例组件
* 展示如何使用useLoader hook控制全局加载状态
*/
const LoaderExample = () => {
// 使用自定义Hook获取加载器控制方法
const { showGlobalLoader, hideGlobalLoader } = useLoader();

// 模拟异步操作
const simulateAsyncOperation = () => {
showGlobalLoader(); // 显示加载器

// 3秒后自动隐藏加载器
setTimeout(() => {
hideGlobalLoader();
}, 3000);
};

return (
<div className="container py-10">
<h1 className="text-2xl font-bold mb-6">全局加载器示例</h1>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle>基本用法</CardTitle>
<CardDescription>
点击按钮显示全局加载器,3秒后自动隐藏
</CardDescription>
</CardHeader>
<CardContent>
<Button onClick={simulateAsyncOperation}>
显示加载器 (3秒)
</Button>
</CardContent>
</Card>

<Card>
<CardHeader>
<CardTitle>手动控制</CardTitle>
<CardDescription>
分别使用按钮手动控制加载器的显示和隐藏
</CardDescription>
</CardHeader>
<CardContent className="flex gap-4">
<Loader/>
<Button onClick={showGlobalLoader} variant="default">
显示加载器
</Button>
<Button onClick={hideGlobalLoader} variant="outline">
隐藏加载器
</Button>
</CardContent>
</Card>

<Card>
<CardHeader>
<CardTitle>实际应用场景</CardTitle>
<CardDescription>
在数据加载过程中显示加载状态
</CardDescription>
</CardHeader>
<CardContent>
<Button
onClick={() => {
showGlobalLoader();
// 模拟API请求
setTimeout(() => {
hideGlobalLoader();
// 显示成功消息
alert('数据加载成功!');
}, 2000);
}}
>
加载数据
</Button>
</CardContent>
</Card>

<Card>
<CardHeader>
<CardTitle>表单提交示例</CardTitle>
<CardDescription>
在表单提交过程中显示加载状态
</CardDescription>
</CardHeader>
<CardContent>
<Button
onClick={() => {
showGlobalLoader();
// 模拟表单提交
setTimeout(() => {
hideGlobalLoader();
// 显示成功消息
alert('表单提交成功!');
}, 1500);
}}
>
提交表单
</Button>
</CardContent>
</Card>
</div>

<div className="mt-8 p-4 bg-gray-100 rounded-md">
<h2 className="text-lg font-semibold mb-2">使用说明</h2>
<p className="mb-2">1. 导入useLoader hook:<code>import {'{ useLoader }'} from '@/hooks/use-loader';</code></p>
<p className="mb-2">2. 在组件中使用hook:<code>const {'{ showGlobalLoader, hideGlobalLoader }'} = useLoader();</code></p>
<p className="mb-2">3. 调用方法显示/隐藏加载器:<code>showGlobalLoader()</code><code>hideGlobalLoader()</code></p>
<p>4. 确保在应用根组件中已引入GlobalLoader组件</p>
</div>
</div>
);
};

export default LoaderExample;
  • Title: React 中实现全局 Loader
  • Author: cccs7
  • Created at : 2025-04-08 23:32:00
  • Updated at : 2025-04-08 23:34:32
  • Link: https://cs7eric.github.io/2025/04/08/React 中实现全局 Loader/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
React 中实现全局 Loader