Next.js getServerSideProps加载状态

np8igboo  于 2022-10-21  发布在  其他
关注(0)|答案(5)|浏览(191)

有没有一种方法可以让我们拥有类似于在client-side上获取数据时的加载状态?
我想要加载状态的原因是具有类似加载 backbone 的东西,例如react-loading-skeleton
在客户端,我们可以执行以下操作:

import useSWR from 'swr'

const fetcher = (url) => fetch(url).then((res) => res.json())

function Profile() {
  const { data, error } = useSWR('/api/user', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

但是对于SSR(GetServerSideProps),我不知道这是否可行,例如,我们可以有一个加载状态吗?

function AllPostsPage(props) {
  const router = useRouter();
  const { posts } = props;

  function findPostsHandler(year, month) {
    const fullPath = `/posts/${year}/${month}`;

    router.push(fullPath);
  }

  if (!data) return <div>loading...</div>; // Would not work with SSR

  return (
    <Fragment>
      <PostsSearch onSearch={findPostsHandler} />
      <PosttList items={posts} />
    </Fragment>
  );
}

export async function getServerSideProps() {
  const posts = await getAllPosts();

  return {
    props: {
      posts: posts,
    },
  };
}

export default AllPostsPage;

最近,Next.js发布了getServerSideProps should support props value as Promisehttps://github.com/vercel/next.js/pull/28607,我们可以做出承诺,但不确定如何实现它,并拥有加载状态,或者这是否可以实现。他们的例子显示:

export async function getServerSideProps() {
  return {
    props: (async function () {
      return {
        text: 'promise value',
      }
    })(),
  }
}
ha5z0ras

ha5z0ras1#

您可以修改_app.js组件以在getServerSideProps执行类似FETCH的异步工作时显示加载组件,如https://stackoverflow.com/a/60756105/13824894所示。这将适用于您的应用程序中的每个页面过渡。
您仍然可以在客户端独立使用加载逻辑。

flmtquvp

flmtquvp2#

您可以在_app.js上设置加载状态

import Router from "next/router";

export default function App({ Component, pageProps }) {
  const [loading, setLoading] = React.useState(false);
  React.useEffect(() => {
    const start = () => {
      console.log("start");
      setLoading(true);
    };
    const end = () => {
      console.log("findished");
      setLoading(false);
    };
    Router.events.on("routeChangeStart", start);
    Router.events.on("routeChangeComplete", end);
    Router.events.on("routeChangeError", end);
    return () => {
      Router.events.off("routeChangeStart", start);
      Router.events.off("routeChangeComplete", end);
      Router.events.off("routeChangeError", end);
    };
  }, []);
  return (
    <>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <Component {...pageProps} />
      )}
    </>
  );
}
ldxq2e6h

ldxq2e6h3#

我还没有尝试过这一功能,但理论上我认为它应该会起作用。如果您想要的只是让客户端通过服务器道具访问Promise,请尝试如下所示。基本上,您的道具是一个异步的lambda函数,因此您可以在其中执行任何必要的工作,例如获取数据等,因此客户端应该将道具作为承诺访问并等待它。

export async function getServerSideProps() {
  return {
    props: (async function () {
           const posts = await getAllPosts();
      return {
        posts: posts,
      }
    })(),
  }
}

//then on client-side you can do the following or similar to set loading state

function MyComponent(props) {

 const [isLoading, setIsLoading] = useState(false);
 const [posts, setPosts] = useState({});

 useEffect(async () => {
   setIsLoading(true);
   const tempPosts = await props?.posts;
   setPosts(posts);
   setIsLoading(false);
}, [])

return (
 {isLoading && <div>loading...</div>}
);

}

export default MyComponent;
pdtvr36n

pdtvr36n4#

我的选择是使用useRouter对象的isReady方法

import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'

function MyApp({ Component, pageProps }) {
  const [isLoading, setIsLoading] = useState(true)
  const router = useRouter()
  useEffect(() => {
    router.isReady && setIsLoading(false)
  }, []

  )
  return <>{isLoading ? <>loading...</> : <Component {...pageProps} />}</>
}

export default MyApp
f5emj3cl

f5emj3cl5#

这对我使用MUI V.5很有效

import Router from "next/router";
import Head from "next/head";
import { useEffect, useState } from "react";
import { CacheProvider } from "@emotion/react";
import {
  ThemeProvider,
  CssBaseline,
  LinearProgress,
  CircularProgress,
  circularProgressClasses,
  Box,
} from "@mui/material";
import { alpha } from "@mui/material/styles";
import createEmotionCache from "/src/createEmotionCache";
import theme from "/src/theme";
import Layout from "/src/components/layout/Layout";

// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();

function Loader(props) {
  return (
    <Box
      sx={{
        position: "fixed",
        top: 0,
        left: 0,
        right: 0,
      }}
    >
      <LinearProgress />
      <Box sx={{ position: "relative", top: 8, left: 8 }}>
        <CircularProgress
          variant="determinate"
          sx={{
            color: alpha(theme.palette.primary.main, 0.25),
          }}
          size={40}
          thickness={4}
          {...props}
          value={100}
        />
        <CircularProgress
          variant="indeterminate"
          disableShrink
          sx={{
            animationDuration: "550ms",
            position: "absolute",
            left: 0,
            [`& .${circularProgressClasses.circle}`]: {
              strokeLinecap: "round",
            },
          }}
          size={40}
          thickness={4}
          {...props}
        />
      </Box>
    </Box>
  );
}

function MyApp({
  Component,
  pageProps,
  emotionCache = clientSideEmotionCache,
}) {
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    Router.events.on("routeChangeStart", () => {
      setIsLoading(true);
    });
    Router.events.on("routeChangeComplete", () => {
      setIsLoading(false);
    });
    Router.events.on("routeChangeError", () => {
      setIsLoading(false);
    });
  }, [Router]);

  return (
    <CacheProvider value={emotionCache}>
      <Head>
        <meta name="viewport" content="initial-scale=1, width=device-width" />
      </Head>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        {isLoading && <Loader />}
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </ThemeProvider>
    </CacheProvider>
  );
}

export default MyApp;

相关问题