简体   繁体   English

Next.js getServerSideProps 加载 state

[英]Next.js getServerSideProps loading state

Is there a way we can have a loading state similar to when fetching data on the client-side ?有没有一种方法可以加载 state 类似于在client-side获取数据时?

The reason I would like a loading state is to have something like a loading-skeleton with for instance react-loading-skeleton我想要加载 state 的原因是有类似加载骨架的东西,例如react-loading-skeleton

On the client-side we could do:在客户端,我们可以这样做:

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

But for SSR (getServerSideProps) I cannot figure out if that is doable for example could we have a loading state?但是对于 SSR (getServerSideProps),我不知道这是否可行,例如我们可以加载 state 吗?

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;

Recently Next.js has released getServerSideProps should support props value as Promise https://github.com/vercel/next.js/pull/28607 With that we can make a promise but am not sure how to implement that and have a loading state or if that is even achievable. Recently Next.js has released getServerSideProps should support props value as Promise https://github.com/vercel/next.js/pull/28607 With that we can make a promise but am not sure how to implement that and have a loading state or if这甚至是可以实现的。 Their example shows:他们的例子表明:

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

I have not tried this feature yet but in theory I think it should work.我还没有尝试过这个功能,但理论上我认为它应该可以工作。 If all you want is to have the client side access to a promise via server props, try as below.如果您只想让客户端通过服务器道具访问承诺,请尝试如下。 Basically your props is a async lambda function so you do any work needed eg fetching data etc inside it so the client-side should access props as a promise and await for it.基本上你的 props 是一个异步 lambda 函数,所以你做任何需要的工作,例如在其中获取数据等,所以客户端应该访问 props 作为承诺并等待它。

    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;

You can modify the _app.js component to show a Loading component while the getServerSideProps is doing async work like a fetch as shown here https://stackoverflow.com/a/60756105/13824894 .您可以修改 _app.js 组件以显示 Loading 组件,而 getServerSideProps 正在执行异步工作(如 fetch),如下所示https://stackoverflow.com/a/60756105/13824894 This will apply on every page transition within your app.这将适用于您应用中的每个页面转换。

You can still use your loading logic client-side independently.您仍然可以独立使用您的加载逻辑客户端。

you can set loading state on _app.js您可以在 _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} />
      )}
    </>
  );
}

My choice is to use isReady method of useRouter object我的选择是使用 useRouter object 的 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM