Next.js] getServerSideProps always return 'undefined' - reactjs

/Pages
/history
/[hid]
/ HistoryDetailContainer.tsx
/ HistoryDetailPresenter.tsx
/ index.tsx
I'm rendering /history/[hid] Page with Container for data, Presenter for view.
But, when I try to fetch data with getServerSideProps at HistoryDetailContainer, it always passes undefined data.
here's my code.
--- Container ---
const HistoryDetailContainer: NextPage<{
ssrData: string;
}> = ({ ssrData }) => {
console.log(`ssr - ${ssrData}`);
...
at the bottom, I'm calling getServerSideProps.
export const getServerSideProps: GetServerSideProps = async (context) => {
try {
const ssrData = 'SSR TEST';
return {
props: {
ssrData,
},
};
} catch (e) {
console.dir(e);
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
};
It always return undefined.
I want to receive data which is not 'undefined' from getServerSideProps.
Anyone know what the problem is?
I need you guys, thanks.

Related

how to destructure property if possibly undefined?

I'm getting stuck on this TS error created at build time. Does anyone has any suggestions?
TypeError: Cannot destructure property 'site' of '(intermediate value)' as it is undefined.
export default function Project({
data,
preview,
}: {
data: any
preview: any
}) {
const { site, page } = data?.post
return (
<Layout site={site} page={page}>
// Stuff
</Layout>
)
}
export async function getStaticProps({ params, preview = false }) {
const { post, morePosts } = await getClient(preview).fetch(projectQuery, {
slug: params.slug,
})
return {
props: {
preview,
data: {
post,
morePosts: overlayDrafts(morePosts),
},
},
}
}
export async function getStaticPaths() {
const paths = await sanityClient.fetch(projectSlugsQuery)
return {
paths: paths.map((slug) => ({ params: { slug } })),
fallback: true,
}
}
You can't destructure it
Better to have an early return (in my opinion), and then continue as normal
if (!data) {
return null
}
const { site, page } = data.post;
// Continue on
...
data?.post will return undefined if post does not exist, so you have to add a fallback object.
const { site, page } = data?.post || {};
You can't destructure without having a source to destructure from, but you can use a default via nullish coalescing:
const { site, page } = data?.post ?? {};
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^
Your current data is of type any, so that should work, but if it had a proper type you might want to provide default values for site and page. For instance if data had the type {post: {site: string; page: number; }; }:
const { site = "something", page = 42 } = data?.post ?? {};
// −−−−−−−−−^^^^^^^^^^^^^^−−−−−−^^^^^−−−−−−−−−−−−−−−^^^^^^

Server-side rendering not working on production Next.js

I am serving my next.js on Vercel (I tried Amplify as well). Page works but components that require data from getServerSideProps() in my pages/index.tsx are not required. It seems that function isn't called at all.
Could anyone help me fix this issue?
export default function Home({ cryptosData, tempObject }) {
return (
<>
{tempObject && <Converter tempObject={tempObject} />}
{cryptosData && <MainTable data={cryptosData} />}
</>
);
}
export const getServerSideProps = async () => {
const url =
"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest";
const query = "?limit=80";
try {
const res = await fetch(url + query, {
headers: { "X-CMC_PRO_API_KEY": process.env.API_KEY },
});
const json = await res.json();
const data = json.data;
const [cryptosData, tempObject] = parseData(data, 40);
return {
props: { cryptosData, tempObject },
};
} catch (error) {
console.log(error);
return {
props: {},
};
}
};
I had the same issue. And the reason was in headers I sent with API requests

Use CSR in Next.js as fallback when SSR path is not available

So I have this blog, with dynamic url for posts (blog/[postId]). Posts are being added through keystone.js cms. It worked fine via CSR, but i came to a necessity to add proper meta open graph tags for each post (for correct share snippets).
I added getStaticProps and getStaticPaths to my post page, and now my meta tags are working fine, but now i get 404 when trying to access any post, that was added after build. Meaning, now i have to rebuild after each new post. My question is - is it possible to set Next to render component as CSR, if it fails to find path for it with getStaticPaths? I've tried to use try catch, but that didn't work. Now i came up with this concept of using container, but it does not seem to be the right way to use fallback. Or is it? I am fairly new to Next, so i am a bit confused.
const PostContainer = (props) => {
const [postData, setPostData] = React.useState(null);
const router = useRouter();
React.useEffect(() => {
if (router.query?.id) {
getPost();
}
}, [router.query]);
const getPost = async () => {
const postData = await getPost(`${router.query.id}`);
if (postData) {
setPostData(postData);
}
};
if (router.isFallback) {
return <Post data={postData}
metaProps={defaultMetaProps}
/>
}
return (
<Post data={postData}
metaProps={props}
/>
);
};
export const getStaticPaths = async () => {
const list = await getPosts();
if (list?.length) {
const paths = list.map((post) => {
return {
params: {
id: post.id,
},
};
});
return {
paths,
fallback: true,
};
}
return {
paths: [],
fallback: true,
};
};
export const getStaticProps = async ({ params }) => {
const { id } = params;
const post = await getPost(id);
const {title, description, previewImage} = post
const props = {
props: {
title,
description,
url: `https://sitename./blog/${id}`,
image: previewImage,
},
revalidate: 1,
};
return props
};
export default PostContainer;
And one more thing - do i understand correctly that if fallback option is true, it takes only one visit to the page by ANY user, after which new path is generated on the server permanently and is accessible for anyone who comes next, therefore fallback always fires just once for this first visit of first user?

SWR hook not reflecting database change

This component is for counting views at page level in Next.js app deployed on AWS Lambda
function ViewsCounter({ slug }: { slug: string }) {
const { data } = useSWR(`/api/views/${slug}`, fetcher);
const views = new Number(data?.total);
useEffect(() => {
const registerView = () =>
fetch(`/api/views/${slug}`, { method: "POST" })
.catch(console.log);
registerView();
}, [slug]);
return (
<>
{views}
</>
);
}
This one is for displaying views on homepage
function ViewsDisplay({ slug }: { slug: string }) {
const { data } = useSWR(`/api/views/${slug}`, fetcher);
const views = new Number(data?.total);
return (
<>
{views}
</>
);
}
While it works as expected on localhost, looks like it displays only the first fetched value and doesn't revalidate it for some reason.
When visiting the page, Counter is triggered correctly and the value is changed in DB.
Probably it has something to do with mutating, any hints are appreciated.
useSWR won't automatically refetch data by default.
You can either enable automatic refetch using the refreshInterval option.
const { data } = useSWR(`/api/views/${slug}`, fetcher, { refreshInterval: 1000 });
Or explicitly update the data yourself using a mutation after the POST request to the API.
function ViewsCounter({ slug }: { slug: string }) {
const { data, mutate } = useSWR(`/api/views/${slug}`, fetcher);
const views = new Number(data?.total);
useEffect(() => {
const registerView = () =>
fetch(`/api/views/${slug}`, { method: "POST" })
.then(() => {
mutate();
})
.catch(console.log);
registerView();
}, [slug]);
return (<>{views}</>);
}

NextJS: How do I get exportPathMap page data? I don't see it in getStaticProps

I have set up exportPathMap but I get an empty object when exporting getStaticProps from my component. I don't understand how I'm supposed to access it?
// next.config.js
exportPathMap: async function (
defaultMapPath,
{ dev, dir, outDir, distDir, buildId }
) {
const paths = {}
const response = await fetch(...)
const data = await response.json()
data.forEach((item) => {
paths[`/item/${item.id}`] = {
page: '/itemPage',
query: {
item,
},
}
})
return paths
},
And
// itemPage.js
...
export async function getStaticProps(props) {
console.log('props', props) // returns "props {}""
return {
props: props
}
}
...
It's difficult to find it specifically stated in the documentation right now, but here are your options.
OPTION 1
Use getStaticPaths and getStaticProps like so (from "with-static-export" in the nextJS examples)
// [id].js post file
export async function getStaticPaths() {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_page=1'
)
const postList = await response.json()
return {
paths: postList.map(post => {
return {
params: {
id: `${post.id}`,
},
}
}),
fallback: false,
}
}
export async function getStaticProps({ params }) {
// fetch single post detail
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${params.id}`
)
const post = await response.json()
return {
props: post,
}
}
OPTION 2
Use exportPathMap AND getInitialProps
// item.js
...
Page.getInitialProps = function (context) {
return context.query.item
}
// next.config.js
module.exports = {
// ...Same as in your question...
}
Both will allow you to have static optimization. With Option 1, you need not export, next build is sufficient. For Option 2 you will need to run next export
From: https://nextjs.org/docs/advanced-features/static-html-export
If your pages don't have getInitialProps you may not need next export at all; next build is already enough thanks to Automatic Static Optimization.
Another note: You can use both methods inside a single project.

Resources