将 NProgress 与“React.lazy”一起使用
Posted
技术标签:
【中文标题】将 NProgress 与“React.lazy”一起使用【英文标题】:Use NProgress with "React.lazy" 【发布时间】:2019-10-31 14:43:13 【问题描述】:我有以下组件树:
<BrowserRouter>
<Suspense fallback=<h1>MyFallback</h1>>
<Switch>
<Route component=HomePage path="/" exact />
<Route
component=lazy(() => import('./pages/Auth/Login'))
path="/auth/login"
exact
/>
</Switch>
</Suspense>
</BrowserRouter>
我使用React.Suspense
来显示加载回退。但是,现在我想在当前页面的顶部显示一个进度条,而不是使用普通的 Suspense 加载回退,这会删除整个当前路由以显示回退。
如何添加 NProgress,例如,指示正在加载的页面的加载进度?
也许新的 React 的并发模式可以帮助解决这个问题? :)
【问题讨论】:
我正在尝试做同样的事情你有没有找到这个问题的答案 不,我没有。 :( @LuizFelipe 现在呢?我也在寻找同样的东西。 @KutsanKaplan,我把这个想法抛在脑后。但也许当新的并发模式发布时,我们也许可以做这样的事情。 【参考方案1】:解决办法
const LazyLoad = () =>
useEffect(() =>
NProgress.start();
return () =>
NProgress.stop();
;
);
return '';
;
<Suspense fallback=<LazyLoad />>
【讨论】:
【参考方案2】:import useEffect from "react";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
export default function TopProgressBar()
useEffect(() =>
NProgress.configure( showSpinner: false );
NProgress.start();
return () =>
NProgress.done();
;
);
return "";
【讨论】:
【参考方案3】:以下内容未经测试,因为我已将其从更高级的配置中提取出来,但它应该可以工作。如果您有困难,请发布,以便我们更新并解决问题。
npm install react-use react-helmet-async nprogress
创建名为“useMounted”的钩子
import useEffect, useRef from 'react';
import useUpdate from 'react-use';
export default function useMounted()
const mounted = useRef(false);
const update = useUpdate();
useEffect(() =>
if (mounted.current === false)
mounted.current = true;
update();
, [update]);
return mounted.current;
创建“ProgressBar”组件
这将允许您传递道具来自定义您的进度条。请注意,这是一个有限的示例,请参阅 NProgress css 文件了解您可能希望修改的其他 css 样式。
import Helmet from 'react-helmet-async';
import useMounted from '../hooks/useMounted'; // your path may differ.
import useLocation from 'react-router-dom'; // not needed for nextjs
import nprogress from 'nprogress';
const ProgressBar = (props?) =>
props =
color: 'red',
height: '2px',
spinner: '20px',
...props
;
// if using NextJS you will not need the below "useMounted" hook
// nor will you need the below "useEffect" both will be
// handled by the Router events in the below Bonus
// monkey patch.
const mounted = useMounted();
const pathname = useLocation(); // assumes react router v6
const [visible, setVisible] = useState(false);
useEffect(() =>
if (!visible)
nprogress.start();
setVisible(true);
if (visible)
nprogress.done();
setVisible(false);
if (!visible && mounted)
setVisible(false);
nprogress.done();
return () =>
nprogress.done();
// eslint-disable-next-line react-hooks/exhaustive-deps
, [pathname, mounted]);
// if using the below styles with NextJS wrap the below in
// <style jsx global>`styles here `</style>;
// you will not need Helmet either simply return the
// jsx style.
const styles = `
#nprogress .bar
background: $props.color;
height: $props.height;
#nprogress .peg
box-shadow: 0 0 10px $props.color, 0 0 5px $props.color;
#nprogress .spinner-icon
width: $props.spinner;
height: $props.spinner;
border-top-color: $props.color;
border-left-color: $props.color;
`;
return (
<Helmet>
<style>styles</style>
</Helmet>
);
;
export default ProgressBar;
使用您的进度条组件
此处显示默认create-react-app 应用程序。
注意:此示例基于 react-router 版本 6
import React from 'react';
import ReactDOM from 'react-dom';
import ProgressBar from './components/ProgressBar'; // your path may differ
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';
import BrowserRouter, Routes from 'react-router-dom';
import './index.css';
import 'nprogress/nprogress.css';
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<ProgressBar />
<Routes>
/* your routes here */
</Routes>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
奖金!猴子补丁抓取触发抓取进度条。
import nprogress from 'nprogress';
// import Router from 'next/router'; // uncomment for NextJS
function DOMEnabled()
return !!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
);
// let timer: NodeJS.Timeout; // for typescript use.
let timer;
let state: string;
let activeRequests = 0;
const delay = 250;
function load()
if (state === 'loading') return;
state = 'loading';
timer = setTimeout(function ()
nprogress.start();
, delay); // only show if longer than the delay
function stop()
if (activeRequests > 0) return;
state = 'stop';
clearTimeout(timer);
nprogress.done();
// Uncomment if using [NextJS][2]
// Router.events.on('routeChangeStart', load);
// Router.events.on('routeChangeComplete', stop);
// Router.events.on('routeChangeError', stop);
if (DOMEnabled())
const _fetch = window.fetch;
window.fetch = async function (...args)
if (activeRequests === 0) load();
activeRequests++;
try
const result = await _fetch(...args);
return result;
catch (ex)
return Promise.reject(ex);
finally
activeRequests -= 1;
if (activeRequests === 0) stop();
;
【讨论】:
【参考方案4】:这是我使用反应钩子的解决方案。
import React, useEffect from 'react';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
const Loading = () =>
useEffect(() =>
NProgress.start();
return () =>
NProgress.done();
;
, []);
return (
<Row>
<Col span=12 offset=6>
Loading
</Col>
</Row>
);
;
export default Loading;
如您所见,我使用useEffect
来检测组件状态。
NProgress.start();
在组件挂载时调用
NProgress.done();
在组件卸载时作为清理调用。
返回值是可选的,你可以渲染任何你想要的。
您还可以使用基于类的组件来实现相同的结果。为此,您可以使用componentWillUnmount()
和componentDidMount()
。
【讨论】:
以上是关于将 NProgress 与“React.lazy”一起使用的主要内容,如果未能解决你的问题,请参考以下文章
react s-s-r 与代码拆分而不是 React.Lazy 的优缺点
React.lazy 会提高 React Native 的性能吗?