Используйте NProgress с "React.lazy"

У меня есть следующее дерево компонентов:

<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 показать запасную загрузку. Однако теперь я хочу показать индикатор выполнения в верхней части текущей страницы вместо использования обычного запасного варианта приостановки загрузки, который удаляет весь текущий маршрут для отображения запасного варианта.

Как мне добавить NProgress, например, чтобы показать ход загрузки загружаемой страницы?

4 ответа

Вот решение

const LazyLoad = () => {
    useEffect(() => {
        NProgress.start();

        return () => {
            NProgress.stop();
        };
    });

    return '';
};

<Suspense fallback={<LazyLoad />}>

Приведенное ниже не тестировалось, так как я вытащил это из более продвинутой конфигурации, однако оно должно работать. Если у вас возникли трудности, отправьте сообщение, чтобы мы могли обновить и решить проблему, спасибо.

      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"

Это позволит вам передавать реквизиты для настройки индикатора выполнения. Обратите внимание, что это ограниченный пример, см. CSS-файл NProgress для дополнительных стилей 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;

Используйте свой компонент Progress Bar

Здесь показано приложение create-реагировать-приложение по умолчанию .

ПРИМЕЧАНИЕ. Этот пример основан на реакции-маршрутизаторе версии 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')
);

Бонус! Выборка патча Monkey для запуска индикатора выполнения при выборке.

      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();
    }
  };
}

      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 "";
}

Вот мое решение, используя реагирующие крючки.

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 определить состояние компонента.

  1. NProgress.start(); вызывается при монтировании компонента
  2. NProgress.done(); вызывается при демонтаже компонента как очистка.

возвращаемое значение не является обязательным, и вы можете сделать все, что вы хотите.

Вы также можете использовать компоненты на основе классов для достижения тех же результатов. чтобы сделать это вы можете использовать componentWillUnmount() а также componentDidMount(),

Другие вопросы по тегам