reactjs Next.js页面转换时加载屏幕

ubof19bj  于 2023-02-08  发布在  React
关注(0)|答案(5)|浏览(159)

我尝试在Next.js应用程序中实现更改路线时的加载屏幕,例如/home -〉/about。
我当前的实现如下:我将初始加载状态设置为false,然后在 componentDidMount 上更改它。我还调用 componentDidMount 中的 Router.events.on 函数,以便在路由更改开始时更改加载状态。
页面文件夹中的***_app.js**

class MyApp extends App {
  constructor(props) {
    super(props);
    this.state = {
      loaded: false,
    };
  }

  componentDidMount() {
    this.setState({ loaded: true });
    Router.events.on('routeChangeStart', () => this.setState({ loaded: false }));
    Router.events.on('routeChangeComplete', () => this.setState({ loaded: true }));
  }

  render() {
    const { Component, pageProps } = this.props;

    const { loaded } = this.state;

    const visibleStyle = {
      display: '',
      transition: 'display 3s',
    };
    const inVisibleStyle = {
      display: 'none',
      transition: 'display 3s',
    };
    return (
      <Container>
        <>
          <span style={loaded ? inVisibleStyle : visibleStyle}>
            <Loader />
          </span>
          <span style={loaded ? visibleStyle : inVisibleStyle}>
            <Component {...pageProps} />
          </span>
        </>
      </Container>
    );
  }
}

这个方法非常好用,但是我觉得可能有更好的解决方案,更优雅的解决方案。这是实现这个加载功能的唯一方法吗?或者有其他选择吗?

x8goxv8g

x8goxv8g1#

使用新的钩子API,这就是我要做的。

function Loading() {
    const router = useRouter();

    const [loading, setLoading] = useState(false);

    useEffect(() => {
        const handleStart = (url) => (url !== router.asPath) && setLoading(true);
        const handleComplete = (url) => (url === router.asPath) && setLoading(false);

        router.events.on('routeChangeStart', handleStart)
        router.events.on('routeChangeComplete', handleComplete)
        router.events.on('routeChangeError', handleComplete)

        return () => {
            router.events.off('routeChangeStart', handleStart)
            router.events.off('routeChangeComplete', handleComplete)
            router.events.off('routeChangeError', handleComplete)
        }
    })
    
    return loading && (<div>Loading....{/*I have an animation here*/}</div>);
}

现在<Loading/>将在路线改变时显示...我使用react-spring制作动画,但是您可以使用任何您喜欢的库来完成此操作。
您甚至可以更进一步,通过修改handleStarthandleComplete方法(获取url)来修改组件显示的时间。

knpiaxh1

knpiaxh12#

为什么不在_app.js中使用nprogress,如下所示

import React from 'react';
import Router from 'next/router';
import App, { Container } from 'next/app';
import NProgress from 'nprogress';

NProgress.configure({ showSpinner: publicRuntimeConfig.NProgressShowSpinner });

Router.onRouteChangeStart = () => {
  // console.log('onRouteChangeStart triggered');
  NProgress.start();
};

Router.onRouteChangeComplete = () => {
  // console.log('onRouteChangeComplete triggered');
  NProgress.done();
};

Router.onRouteChangeError = () => {
  // console.log('onRouteChangeError triggered');
  NProgress.done();
};

export default class MyApp extends App { ... }

链接到nprogress
您还需要包含样式文件。如果您将css文件放在static目录下,则可以按如下方式访问样式:

<link rel="stylesheet" type="text/css" href="/static/css/nprogress.css" />

确保CSS在所有页面中可用...
它将适用于您所有的路线变化。

btqmn9zl

btqmn9zl3#

对于任何在2021年遇到这个问题的人来说,nextjs-progressbar包让这个问题变得超级简单。在您的Next.js _app.js中,只需添加:

import NextNProgress from 'nextjs-progressbar';

export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <NextNProgress />
      <Component {...pageProps} />;
    </>
  );
}

搞定!
Demo和屏幕截图:

kr98yfug

kr98yfug4#

NProgress的新更新:

import Router from 'next/router'
import Link from 'next/link'
import Head from 'next/head'
import NProgress from 'nprogress'

Router.events.on('routeChangeStart', (url) => {
  console.log(`Loading: ${url}`)
  NProgress.start()
})
Router.events.on('routeChangeComplete', () => NProgress.done())
Router.events.on('routeChangeError', () => NProgress.done())

export default function App({ Component, pageProps }) {
  return (
    <>
      <Head>
        {/* Import CSS for nprogress */}
        <link rel="stylesheet" type="text/css" href="/nprogress.css" />
      </Head>
      <Component {...pageProps} />
    </>
  )
}

如果您使用Tailwind CSS,请从此处复制代码:https://unpkg.com/nprogress@0.2.0/nprogress.css并将代码粘贴到全局CSS文件中。
如果你想禁用微调器,请在_app.tsx/jsx文件中添加以下代码,并从CSS中删除微调器样式。

NProgress.configure({ showSpinner: false });

来源链接:

7fhtutme

7fhtutme5#

进度条,如NProgress,90行代码(与NProgress v0.2.0相比,为470行. js +70行. css):

import { useEffect, useReducer, useRef } from 'react';

import { assert } from './assert';
import { wait } from './wait';
import { getRandomInt } from './getRandomNumber';

let waitController: AbortController | undefined;

// https://gist.github.com/tkrotoff/db8a8106cc93ae797ea968d78ea28047
export function useProgressBar({
  trickleMaxWidth = 94,
  trickleIncrementMin = 1,
  trickleIncrementMax = 5,
  dropMinSpeed = 50,
  dropMaxSpeed = 150,
  transitionSpeed = 600
} = {}) {
  // https://stackoverflow.com/a/66436476
  const [, forceUpdate] = useReducer(x => x + 1, 0);

  // https://github.com/facebook/react/issues/14010#issuecomment-433788147
  const widthRef = useRef(0);

  function setWidth(value: number) {
    widthRef.current = value;
    forceUpdate();
  }

  async function trickle() {
    if (widthRef.current < trickleMaxWidth) {
      const inc =
        widthRef.current +
        getRandomInt(trickleIncrementMin, trickleIncrementMax); // ~3
      setWidth(inc);
      try {
        await wait(getRandomInt(dropMinSpeed, dropMaxSpeed) /* ~100 ms */, {
          signal: waitController!.signal
        });
        await trickle();
      } catch {
        // Current loop aborted: a new route has been started
      }
    }
  }

  async function start() {
    // Abort current loops if any: a new route has been started
    waitController?.abort();
    waitController = new AbortController();

    // Force the show the JSX
    setWidth(1);
    await wait(0);

    await trickle();
  }

  async function complete() {
    assert(
      waitController !== undefined,
      'You need to ensure start() is called before calling complete()'
    );
    setWidth(100);
    try {
      await wait(transitionSpeed, { signal: waitController.signal });
      setWidth(0);
    } catch {
      // Current loop aborted: a new route has been started
    }
  }

  function reset() {
    // Abort current loops if any
    waitController?.abort();
    setWidth(0);
  }

  useEffect(() => {
    return () => {
      // Abort current loops if any
      waitController?.abort();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return {
    start,
    complete,
    reset,
    width: widthRef.current
  };
}
import { useRouter } from 'next/router';
import { useEffect } from 'react';

import { useProgressBar } from './useProgressBar';

const transitionSpeed = 600;

// https://gist.github.com/tkrotoff/db8a8106cc93ae797ea968d78ea28047
export function RouterProgressBar(
  props?: Parameters<typeof useProgressBar>[0]
) {
  const { events } = useRouter();

  const { width, start, complete, reset } = useProgressBar({
    transitionSpeed,
    ...props
  });

  useEffect(() => {
    events.on('routeChangeStart', start);
    events.on('routeChangeComplete', complete);
    events.on('routeChangeError', reset); // Typical case: "Route Cancelled"

    return () => {
      events.off('routeChangeStart', start);
      events.off('routeChangeComplete', complete);
      events.off('routeChangeError', reset);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return width > 0 ? (
    // Use Bootstrap, Material UI, Tailwind CSS... to style the progress bar
    <div
      className="progress fixed-top bg-transparent rounded-0"
      style={{
        height: 3, // GitHub turbo-progress-bar height is 3px
        zIndex: 1091 // $zindex-toast + 1 => always visible
      }}
    >
      <div
        className="progress-bar"
        style={{
          width: `${width}%`,
          //transition: 'none',
          transition: `width ${width > 1 ? transitionSpeed : 0}ms ease`
        }}
      />
    </div>
  ) : null;
}

使用方法:

// pages/_app.tsx

import { AppProps } from 'next/app';
import Head from 'next/head';

import { RouterProgressBar } from './RouterProgressBar';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Head>
        <title>My title</title>
        <meta name="description" content="My description" />
      </Head>

      <RouterProgressBar />

      <Component {...pageProps} />
    </>
  );
}

更多信息:https://gist.github.com/tkrotoff/db8a8106cc93ae797ea968d78ea28047

相关问题