Next.js getStaticProps not returning data - reactjs

I am creating a blog in Next.js and using Strapi headless CMS as backend.
I was trying to get data from the API I made from Strapi.
For fetching data I made
./client.js
export default class StrapiClient{
constructor(){}
/*API_URL = "http://localhost:1337/api/"*/
async fetchData(path){
const url = `${process.env.API_URL}${path}`
const res = await fetch(url)
const posts = await res.json()
return posts
}}
and imported it to ./components/blog.js
import StrapiClient from '../client'
const Client = new StrapiClient
export const getStaticProps = async () => {
const posts = await Client.fetchData(`articles`)
return{
props: {
posts,
}
}
};
const Blog = ({posts}) => {
return (
<div>
{posts.data.map((element) => {
return(
<div key={element.id}>
<h1 className=" text-2xl">{element.attributes.title}</h1>
</div>
)
})}
</div>
);
};
export default Blog;
but I got error
TypeError: Cannot read properties of undefined (reading 'data')
and here is the structure of data I was using
{
"data" : [
"id" /*string*/
]
}

You need to await the async function to get data from Promise
export const getStaticProps = async () => {
const posts = await Client.fetchData(`articles`)
return{
props: {
posts,
}
}
};
Async functions always return a promise
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

const posts = Client.fetchData(`articles`)
I think you need to await for the fetchData.
Edit:
I just noticed you are using getStaticPaths function instead of getStaticProps. Can you please change the name and try again?

From next.js documentation the getStaticPaths method is used to define a list of paths to be statically generated but to fetch data for page you need to use getStaticProps:
export async function getStaticProps() {
const posts = await Client.fetchData(`articles`);
return {
props: {
posts,
},
}
}
Or fetch data using getServerSideProps without use getStaticPaths:
export async function getServerSideProps() {
const posts = await Client.fetchData(`articles`);
return { props: { posts } }
}

Related

Problem with generation of dynamic pages in Next.JS. Error 404 gets printed instead

I'm trying to learn Next.JS and after learning about getStaticPaths() I wanted to use it in a 'real life example'. However, contrary to the examples made with JSONPlaceholder it seems that I can't get it to work, and instead I get a 404 error with no messages whatsoever. So I wanted to ask what am I doing wrong:
is it the code itself (there's an error I can't see)
is it the approach itself?
index.js
import Link from 'next/dist/client/link';
const CHAMPIONS_URL =
'https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json';
export async function getStaticProps() {
const res = await fetch(CHAMPIONS_URL);
const champions = await res.json();
return {
props: { champions },
};
}
const Champions = ({ champions }) => {
// console.log(champions);
return (
<ul>
{champions.slice(1).map((champion) => (
<li key={champion.id}>
<Link href={`/champions/${champion.name}`}>{champion.name}</Link>
</li>
))}
</ul>
);
};
export default Champions;
[id].js
const CHAMPIONS_URL =
'https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json';
function getChampion(champion) {
return `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champions/${champion}.json`;
}
export async function getStaticPaths() {
const res = await fetch(CHAMPIONS_URL);
const champions = await res.json();
const paths = champions.map((champion) => ({ params: { id: champion.id.toString() } }));
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
const res = await fetch(getChampion(params.id));
const champion = await res.json();
return { props: { champion } };
}
const Champion = ({ champion }) => {
console.log(champion);
return <div>Champion</div>;
};
export default Champion;
Your approach is fine. The 404 error you're seeing doesn't originate from Next.js itself, but from the external endpoint inside your getChampion() function in ./pages/champions/[id].js (and make sure [id].js exists in a directory named champions).
Passing champion.name to ${champion}.json will result in an error after interpolation since you're expecting to use params.id:
https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champions/Annie.json
But passing champion.id instead will work:
https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champions/1.json
So you really just need to change your <Link> component in .pages/index.js from...
{/* if you use `name` this will error */}
<Link href={`/champions/${champion.name}`}>{champion.name}</Link>
...to:
{/* but if you use `id` (since your page is named `[id].js`) this should work */}
<Link href={`/champions/${champion.id}`}>{champion.name}</Link>

getServerSideProps not working in nextjs error in mapping

I am trying to do getServerSideProps but I am getting the following error what is the error I am doing
TypeError: Cannot read properties of undefined (reading 'map')
import React from "react";
import axios from "axios";
import { useState, useEffect } from "react";
import { FormControl, Button } from "react-bootstrap";
import Card from "react-bootstrap/Card";
export default function Answershooks(props, { posts }) {
return (
<div className="answerhook">
{posts.map((personData, index) => {
return (
<Card key={index} className="cardmobile">
<Card.Body>
<p className="answersize">{personData.Answers} </p>
</Card.Body>
</Card>
);
})}
</div>
);
}
export async function getServerSideProps(ctx) {
const res = await fetch("https://askover.wixten.com/answersapi/" + props.id);
console.log(res);
console.log("dada");
const posts = await res.json();
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
};
}
i have added added a file stucture screenshot so u undersand how my files are placed
Your main problem is you're trying to call getServerSideProps in Answerhooks but it's not a page component, so you cannot get data on the server as expected
Instead of having getServerSideProps in that, you can move your API call to getServerSideProps in [itmid].jsx (which is an actual page component) like below
export async function getServerSideProps(ctx) {
var id = ctx.query.itmid;
const queryRequest = fetch("https://ask-over.herokuapp.com/questone/" + id).then(async (res) => await res.json());
const answerRequest = fetch("https://askover.wixten.com/answersapi/" + id).then(async (res) => await res.json());
const [posts, answerPosts] = await Promise.all([queryRequest, answerRequest]);
return {
props: {
posts,
answerPosts
}
};
}
After that, you can get answerPosts from props for Query
function Query({ posts, answerPosts }) {
return <Answerhooks answerPosts={answerPosts} />
}
Finally, you can have the data on props inside Answerhooks component
function Answershooks({ answerPosts }) {
//TODO: Apply your logic with `answerPosts`
console.log(answerPosts)
return <div></div>
}
Lets start with the fetch error and work out why that is failing so make a new component.
fetchHandler.js
export async function fetchHandler(url){
try{
const res = await fetch(url);
return res
} catch(err){
console.log(err); //this will tell us why it failed.
return false //this gives us a condition we can use on the front end
}
}
Then your Answerhooks.
import {fetchHandler} from '../yourpath'
export default function Answershooks({ posts }) {
return (
<div className="answerhook">
{posts.map((personData, index) => {
return (
<Card key={index} className="cardmobile">
<Card.Body>
<p className="answersize">{personData.Answers} </p>
</Card.Body>
</Card>
);
})}
</div>
);
}
export async function getServerSideProps(ctx) {
const url = `https://askover.wixten.com/answersapi/${ctx.query.id}`
const res = await fetchHandler(url)
console.log(res);
const posts = await res.json();
return {
props: {
posts: posts === false ? [] : posts //Just to stop your app crashing
},
};
}
export const getServerSideProps = wrapper.getServerSideProps(
(store) =>
async ({req}) => {
const result = await store.dispatch(fetchHome());
return {
props: {
list : result
},
};
}
);

How to access current language in getStaticProps Next.js?

I'm using i18n in my next app, I need to access the current page language in getStaticProps then i fetch data
export const getStaticProps = async () => {
//need to get language here
return {
props: { data },
};
};
const App = ({ data }) => {
//my component where i can get language
const { t, i18n } = useTranslation();
const currentLang = i18n.language;
};
You can get the local from the object that is passed to the getStaticProps.
You can see it in this example from Vercel
In you case it could look like this:
export const getStaticProps = async ({ locale }) => {
doSomethingWithLocale(locale)
return {
props: { data },
};
};

getStaticProps returns data not defined In nextjs

I want to fetch single data by id and I am using getStaticPaths and getStaticProps but I am getting the error data is not defined. Where am I going wrong Please help
My [id].tsx file
import MainComponentLayout from "../../components/Layout/MainLayoutComponent"
import EditProject from "../../components/EditProjectForm";
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`)
const data = await response.json()
console.log(data)
const path = data.result.map(project => {
return{
params: {id:project.project_ID}
}
})
return{
paths:path,
fallback: false
}
}
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects/${id}`)
// Single Object
const data = await response.json()
return{
props: {fetchedData:data},
}
}
const EditForm = () => {
return(
<MainComponentLayout ComponentToRender = {<EditProject fetchedData = {fetchedData}/>}/>
)
}
export default EditForm
Change const EditForm = () => { to const EditForm = ({fetchedData}) => and it will work.
The getStaticProps, as its name implies, passes the fetched props object to the function as properties. You need to define them in the function as an object, and you can also destructure as in the example above, basically defining a fetchedData variable.
If You want to use props {fetchedData:data} in your app, You need pass them to the page component as props. As we can read in docs:
props - An optional object with the props that will be received by the
page component. It should be a serializable object
Here You have example page with getStaticProps() correctly used.
and Your code with props, Good Luck ! ;-)
import MainComponentLayout from "../../components/Layout/MainLayoutComponent";
import EditProject from "../../components/EditProjectForm";
const EditForm = ({ fetchedData }) => {
return (
<MainComponentLayout
ComponentToRender={<EditProject fetchedData={fetchedData} />}
/>
);
};
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id;
const response = await fetch(
`http://b560-45-248-23-129.ngrok.io/projects/${id}`
);
// Single Object
const data = await response.json();
return {
props: { fetchedData: data },
};
};
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`);
const data = await response.json();
console.log(data);
const path = data.result.map((project) => {
return {
params: { id: project.project_ID },
};
});
return {
paths: path,
fallback: false,
};
};
export default EditForm;

Persist data fetched in Nextjs' getServerSideProps to rest of app without having to re-fetch on page load

I have an index page that includes data fetching within getServerSideProps.
If I use next/link or maybe router.push() - Is there a way for that data to persist across to the rest of the pages in my app?
Or is this a scenario where I'd need to use something like Context/Redux?
For example:
index.tsx
const App = ({ productData }: IndexProps) => {
return (
<Link href={`/product/${title}`}> ... </Link>
)
}
export const getServerSideProps: GetServerSideProps = async () => {
const productData = await getProducts();
return {
props: { productData },
};
};
/product/[id].tsx
const Product = ({ productData }) => {
return (
<Link href={`/product/${title}`}> ... </Link>
)
}
export const getServerSideProps: GetServerSideProps = async () => {
if (PRODUCTDATADOESNTEXIST) {
const productData = await getProducts();
}
// else use data fetched in from previous page?
return {
props: { productData },
};
};
Thanks!
You can create a service that will cache the data in memory.
Something like this.
const cache = {};
export const setProducts = (products) => {
products.forEach((p) => {
cache[p.id] = p;
});
};
const getProduct = (id) {
if(cache[id]){
Promise.resolve(cache[id]);
}
// make the API call to fetch data;
}
export default getProduct;
Use the set method to store the data from the NextJS Page and use the get method to fetch data when needed.

Resources