Is there a way we can have a loading state similar to when fetching data on the client-side
?
The reason I would like a loading state is to have something like a loading-skeleton with for instance 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?
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. 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.
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 . 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
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} />
)}
</>
);
}
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.