使用 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 = () => { 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 = () => { 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';
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";
const LoaderExample = () => { const { showGlobalLoader, hideGlobalLoader } = useLoader();
const simulateAsyncOperation = () => { showGlobalLoader(); 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;
|