I am new to nextjs. I want to prerender a page on server and want to delay rendering till API call is resolved. to achieve this i am using getStaticProps as mentioned in nextjs official docs.
here is the position of the file in my code structure:-
i am exporting my getStaticProps from index.js
here is the code snippet :-
export const getStaticProps = async () => {
const res = await axios.get(`http://blogexample.com/blog/posts`);
const blogList = await res.data
return {
props: {
blogList
}
}
}
const Blog = (props) => {
const { blogList } = props;
useEffect(() => {
console.log('list',blogList)
},[blogList])
return(
....
);
}
export default Blog;
problem is that, in browser console....my console.log('list',blogList) statement prints undefined
what i am doing wrong
getStaticProps is executed during build time. Use getServerSideProps
instead.
Related
I am fetching all posts from the backend from the API call http://localhost:3000/api/jobs and it is working perfectly by using getstaticProps(). Now I want to get a particular post based on slug and my API call is http://localhost:3000/api/jobs/:slug and it perfectly working API call But whenever I used the code shown below for dynamic routes it shows me a server error on the frontend and unable to fetch particular post data.
[slug].js
export const getStaticPaths = async () => {
const res = await fetch(`${API}/jobs`);
const post = await res.json();
const paths = post.map(job => {
return {
params: { slug: job.slug }
}
})
return {
paths,
fallback:true
}
}
export const getStaticProps = async (ctx) => {
const slug = ctx.params.slug;
const [job, photo] = await Promise.all([
fetch(`${API}/jobs/${slug}`).then(r => r.json()),
`${API}/jobs/photo/${slug}`
]);
if (!job) {
return {
notFound:true
}
}
return {
props: {
job,
photo
},
revalidate:60
}
}
Also whenever I used another API call like http://localhost:3000/api/jobs-edit which has the same function as that of http://localhost:3000/api/jobs inside getStaticPaths() then it performs well and gives us single post data.
What can be the problem?
The problem:
getServerSideProps is blocking the whole site on subsequent requests, even when only the props are requested and all other js is already loaded.
So I was wondering if it is possible to add a loading component to each page (something like the dynamic layout: https://github.com/vercel/next.js/tree/canary/examples/layout-component)
and show it instantly while waiting till the props are loaded.
I know about the Router from nextjs and the events but how would I show the loader when I got only the url on routeChangeStart?
I use getInitialProps now. Thought it was deprecated, but it's not.
I can now do something like this:
const MyPage = (props) => {
const [data, setData] = useState(null)
useEffect(() => {
if (props.data) {
setData(props.data)
} else {
// fetch client side and setData
}
});
return (
<>
{data === null && <LoadingMyPage/>}
{data && <TheActualContent/>}
</>
);
}
MyPage.getInitialProps = async () => {
if (window) return {data: undefined};
// fetch server side
return {data: ...}
}
Do I need to use getStaticPaths if I'm not going to run the "next export" command?
Can I create a cache structure on the server side using getStaticProps and revalidate for the detail page (/user/1)? Or is there no alternative other than using SWR or getServerSideRender?
Isn't it redundant to get all the data again in the detail(/user/1) page?
Note: User detail page can be refreshed every 60 seconds. Instant update is not important.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
const paths = posts.map((post) => ({
params: { id: post.id },
}))
return { paths }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
No . getStaticPaths is useful when it comes to statically generating lot's of pages incrementally .
Yes in next js you can use revalidate cache strategy only when you are generating static pages with getStaticProps like this :
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`);
const post = await res.json();
// revalidate value is basically in seconds so here it will revalidate the
// data every 1 second only If there is an update to it .
return { props: { post } , revalidate : 1 }
}
You can use getStaticProps method with getStaticPaths to incrementally generate all pages you need and use pagination .
For example, I have a dynamic route /blog/[article-id].
When visiting an existing blog post /blog/id-that-exist, it works as expected, and now I want to handle the case /blog/id-that-does-not-exist properly.
The code in /blog/[id].jsx looks something like:
export const getStaticPaths async () => {
return {
fallback: true,
paths: (await sequelize.models.Article.findAll()).map(
article => {
return {
params: {
pid: article.slug,
}
}
}
),
}
}
export const getStaticProps async () => {
// Try to get it from the database. Returns none if does not exist.
const article = await sequelize.models.Article.findOne({
where: { slug: pid },
});
return { props: { article: article } };
}
const ArticlePage = (props) => {
// This can happen due to fallback: true while waiting for
// a page that was not rendered at build time to build.
const router = useRouter()
if (router.isFallback) {
return <div>loading</div>;
}
return (
<div>{props.article.body}</div>
);
};
export const getStaticPaths = getStaticPathsArticle;
export const getStaticProps = getStaticPropsArticle;
export default ArticlePage;
I saw this related question: How to handle not found 404 for dynamic routes in Next.js which is calling API? but I'm not sure if it's the same as I'm asking here, as this does not depend on any external API being used.
notFound: true from Next.js 10
Starting in Next.js 10, we can do:
export const getStaticProps async () => {
// Try to get it from the database. Returns none if does not exist.
const article = await sequelize.models.Article.findOne({
where: { slug: pid },
});
if (!article) {
return {
notFound: true
}
}
return { props: { article: article } };
}
as documented at: https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
When notFound is returned, the rendering function ArticlePage just never gets called, and the default 404 page is returned instead.
Note however that ArticlePage did get
For some reason in development mode:
I don't get the expected 404 HTTP status code
ArticlePage, so if you forgot to handle the fallback case, the it might crash due to missing properties
which was confusing me a bit. But in production mode, everything works as expected.
Workaround before Next.js 10
As shown https://github.com/vercel/next.js/discussions/10960#discussioncomment-1201 you could previously do something like:
const ArticlePage = (props) => {
if (!props.article) {
return <>
<Head>
<meta name="robots" content="noindex">
</Head>
<DefaultErrorPage statusCode={404} />
</>
}
return (
<div>{props.article.body}</div>
);
};
but this is not ideal because it does not set the HTTP return code correctly I believe, and I don't know how to do it.
Tested on Next.js 10.2.2.
I've read your answer regarding the solution after Next.js v.10, but I didn't get what was the problem in showing the expected http 404 code during development.
I use Next.JS v.12 and I get the expected 404 normally in development
import { GetStaticPaths, GetStaticProps } from 'next'
import { useRouter } from 'next/router'
import { ParsedUrlQuery } from 'querystring'
import Loading from '../../components/loading'
export const getStaticPaths: GetStaticPaths = async () => {
//your paths
return { paths, fallback: true }
}
export const getStaticProps: GetStaticProps = async ({ params }: { params?: ParsedUrlQuery }) => {
//get your props
if (!target){
return {notFound: true}
}
return { props: { ... }, revalidate: 86400}
}
function Index({ ... }) {
const router = useRouter()
if (router.isFallback) {
return <Loading />
}
return (
<div>
//my content
</div>
)
}
export default Index
When the target isn't found, it renders my custom 404 component in pages/404.tsx if I created one or just the default 404 page.
This should work normally during development and production.
I have been trying to persist my redux store through a reload. I was using useEffect to dispatch my actions at first but then when I tried to reload the page router became undefined and I got a 500 error. After that I tried using getInitialProps and use the ctx.query.id but I ran into another error saying that hooks can only be called inside of the body of a function component.
How do I make it so hooks work inside of getInitialProps and what is the best way of persisting my store data through a reload?
export default function CarPage() {
const dispatch = useDispatch()
const router = useRouter()
const car = useSelector((state) => state.cars.car)
/*
useEffect(() => {
if(!car && router) {
dispatch(getCar(router.query.id))
}
}, [])
*/
return (
<Container>
<h2>{car.model}</h2>
</Container>
)
}
CarPage.getInitialProps = async (ctx) => {
const dispatch = useDispatch()
dispatch(getCar(ctx.query.id))
}
To persist redux store through a page reload, we definitely need to use browser storage.
I suggest using https://github.com/rt2zz/redux-persist.
To use dispatch inside getInitialProps, please try with this code snippet instead of using useDispatch() hook.
CarPage.getInitialProps = async ({ store, query }) => {
store.dispatch(getCar(query.id));
return { initialState: store.getState() };
}