getStaticPaths for dynamic route catching all routes [[...Id]].js in nextjs - reactjs

I developed small application with catch all route method url is http://localhost:3000/category/fashion/19see the bellow image
Now I am using getServerSideProps method working fine, and changed to getStaticProps method error displayed, bu want use getStaticPaths method, I got dynamic values using context.params.Id[0]=fashion and context.params.Id1=19, But can not call this value inside of getStaticPaths, What I am missing and can you please solve this issue please.
category/[...Id].js
import { useRouter } from 'next/router'
import Head from 'next/head'
import { Grid, Image,Segment, Card } from "semantic-ui-react"
import 'semantic-ui-css/semantic.min.css'
import Layout from '../../components/layout'
const Post = (props) => {
const router = useRouter()
const { Id } = router.query
console.log(props.category_list)
return(
<>
<Layout>
<Head>
<meta charSet="utf-8" />
<title>Test</title>
{/* <meta name="description" content={seo_description} /> */}
<meta name="keywords" content=""/>
<meta name="google-site-verification" content="rtIRrUNRpgZ_lFtlfS8LJ0-8j_d569BXS9NOGa_nM6Y" />
</Head>
<Grid className="store-list">
<Grid.Column width={16}>
<p>
<span>{props.category_title.heading_label}</span>
</p>
</Grid.Column>
</Grid>
<Grid columns={4} className="all-offers storeList">
{props.category_list.map((x) => {
return (
<Grid.Column>
<Segment>
<Card>
<a href ={x.store_url}>
{" "}
<Image
src={x.image}
alt=""
className="collection-img"
></Image>
</a>
<Card.Content>
<a href ={x.store_url}>
{" "}
<Card.Header>{x.name}</Card.Header>
</a>
<Card.Description>{x.store_summary}</Card.Description>
</Card.Content>
<Card.Content extra>
<p className="rewards">
<span>
<img src="/images/rewards.png" alt=""></img>
</span>
Cash rewards upto <span>AED {x.cashback}</span>
</p>
<p className="location">
<span>
<img src="/images/location.png" alt=""></img>
</span>
<span className="store-location" key="index">{x.store_branches}</span>
{/* {x.store_branches.map((locations, index) => (
<span className="store-location">
{index === 0 ? (
<span>{locations.store_location}</span>
) : index >= 1 ? (
<span>
, {locations.store_location}
</span>
) : null}
</span>
))} */}
</p>
</Card.Content>
</Card>
</Segment>
</Grid.Column>
);
})}
</Grid>
</Layout>
</>
)
}
export async function getStaticPaths() {
// How to call context value here
return { paths, fallback: 'blocking' }
}
export async function getStaticProps(context) {
const id = context.params.Id[1];
const postBody = {
category_id: id,
offer_type: "",
};
const offerList = await fetch('https://lenapp.ae/api/v5/web/list',{
method:'POST',
body: JSON.stringify(postBody),
headers: { "Content-Type": "application/json" },
})
const category = await offerList.json();
// const bookJson = JSON.stringify(book)
// const bookJson=offerData.stores;
const category_list=category.stores;
const category_title=category;
return {
props: {
category_list,
category_title
}
};
}
export default Post;

export async function getStaticPaths() {
//How to call context value here - explaining
const res = await fetch('your_api_path')
const posts = await res.json()
//params: {id: post.id} - this is your value will be fashion or 19, check the data/field you receiving
const paths = posts.map((post) => ({
params: { id: post.id },
}))
return { paths, fallback: 'blocking' }
}
For the nested dynamic routes: You can create /category/[style](folder)/[id](file). I suggest you make firstly with getServerSideProps all your pages and routing, and then do with getStaticProps.

Related

UseState value doesn't change when re-render in NextJs

I am creating a blog application on NextJs and on the page which displays posts by category i faced with a problem
i have a useState which contains all posts that i get from backend by category that i pass
The problem is when i click on the link which changes category of displayed posts i still got same posts on page, and to get actual useState value i have to reload page, how to fix it?
Here is full component
const News: FC<NewsCat> = ({ news, category }) => {
const [selectedCateg, setSelectedCateg] = useState(category);
//UseState with posts
const [postsList, setPostsList] = useState(news);
const [page, setPage] = useState(1);
const handleClick = (categ: string) => {
setSelectedCateg(categ);
};
return (
<div className={styles.wrap}>
<nav className={styles.categories}>
//Cetegory list
{list.map((i: string, index) => (
<Link href={"/category/" + listEng[index]} key={index}>
<button
className={styles.active}
onClick={() => handleClick(listEng[index])}
>
<h2>{i}</h2>
</button>
</Link>
))}
</nav>
<div className={styles.catTitle}>
<h1>{newsTranslate(selectedCateg)}</h1>
</div>
<div className={styles.mainBlock}>
{postsList.map((i: News) => (
<div key={i._id} className={styles.normal_card}>
<div className={styles.normal_card_img}>
<Link href={"/news/" + i._id}>
<img src={i?.image} alt="" />
</Link>
<div className={styles.desc}>
<div className={styles.up_desc}>
<Link href={"/category/" + category}>
<h6>{newsTranslate(i.category)}</h6>
</Link>
<h6>| {moment(i.createdAt).format("LLL")}</h6>
</div>
<Link href={"/news/" + i._id}>
<h2 className={styles.titleDesc}>
{i.title?.length > 150
? `${i.title?.substring(0, 90)}...`
: i.title}
</h2>
</Link>
</div>
</div>
<div className={styles.normal_card_desc}>
<h4>{moment(i.createdAt).format("LLL")}</h4>
</div>
</div>
))}
</div>
<div className={styles.loadMoreButton}>
<button
onClick={async () => {
setPage(page + 1);
console.log(page);
const getNextPosts = await axios.get(
"http://localhost:3000/api/category/" + category,
{
params: {
page: page,
},
}
);
setPostsList([...postsList, ...getNextPosts.data]);
}}
>
LoadMore
</button>
</div>
</div>
);
};
export default News;
export const getServerSideProps: GetServerSideProps = async ({
params,
}: any) => {
const res = await axios.get(
`http://localhost:3000/api/category/${params.category}`,
{
params: {
page: 1
}
}
);
return {
props: {
news: res?.data,
category: params?.category,
},
};
};
I know that i can fix it like this:
useEffect(() => {
setPostsList(news)
}, [news])
but in my opinion its not the best way
postsList will only change value when you call setPostsList (except for the initial value which you're passing in on first load).
So after the category is changed, you'll need to fetch the posts from the API and call setPostsList (similar to how you are doing on page change)

How to fix (Uncaught Error: Cannot find module './undefined.jpg') in React.js

I would appreciate to know why it gives this './undefined.jpg' before anything else and only AFTER that, renders all the actual paths.
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import style from './CarsListPage.module.scss';
//import cars from './car-content';
import CarsList from '../components/CarsList';
const CarsListPage = () => {
const [carsInfo, setCarsInfo] = useState([{}]);
useEffect(() => {
const loadCarsInfo = async () => {
const response = await axios.get('/api/cars');
const newCarsInfo = response.data;
setCarsInfo(newCarsInfo);
};
loadCarsInfo();
}, []);
return (
<div className={style.mainCotainer}>
<main className={style.main}>
<h1>Cars</h1>
<div className={style.container}>
{carsInfo.map((car) => (
<Link to={`/cars/${car.name}`} key={car.id}>
<div className={style.card} key={car.id}>
<h3>{car.name}</h3>
{/* {console.log(`../temp-img/${car.title}.jpg`)} */}
<p>{car.body_type}</p>
<p>{car.origin}</p>
<p>{car.year}</p>
<img
src={require(`../temp-img/${car.title}.jpg`)}
alt={car.name}
style={{ width: '200px' }}
/>
</div>
</Link>
))}
</div>
</main>
</div>
);
};
export default CarsListPage;
I've found couple solutions like wrapping everying into div and check whether value exists or not but i could not optimize it for my code.
Change the default state of carsInfo to [] otherwise you will map on an empty object until you get the data from the API:
const CarsListPage = () => {
const [carsInfo, setCarsInfo] = useState([]);
useEffect(() => {
const loadCarsInfo = async () => {
const response = await axios.get('/api/cars');
const newCarsInfo = response.data;
setCarsInfo(newCarsInfo);
};
loadCarsInfo();
}, []);
return (
<div className={style.mainCotainer}>
<main className={style.main}>
<h1>Cars</h1>
<div className={style.container}>
{carsInfo.length && carsInfo.map((car) => (
<Link to={`/cars/${car.name}`} key={car.id}>
<div className={style.card} key={car.id}>
<h3>{car.name}</h3>
{/* {console.log(`../temp-img/${car.title}.jpg`)} */}
<p>{car.body_type}</p>
<p>{car.origin}</p>
<p>{car.year}</p>
<img
src={require(`../temp-img/${car.title}.jpg`)}
alt={car.name}
style={{ width: '200px' }}
/>
</div>
</Link>
))}
</div>
</main>
</div>
);
};

How to dynamically fetch api route from component in page folder?

i have a reusable contact form that works perfectly when used in the index.js file.
However when i use it from a component in the page folder i am having a 404 not found error message because it uses this route 3000/ourServices/conciergerie/api/contact/ instead of 3000/api/contact.
How do i ensure the it will always fetch the correct route? please see how i fetch the api below :
async function handleSubmit() {
const data = {
firstName,
email,
phone,
message,
};
const res = await fetch("api/contact", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: data,
token: "test",
}),
});
alert("Message sent! Thank you\nWe will be in touch with you soon!");
}
pages/ourServices/conciergerie
import Image from "next/image";
import { AiOutlinePlus, AiOutlineMinus } from "react-icons/ai";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { Contact } from "../../components/contact/Contact";
import es from "../../locales/es-ES/conciergerie.json";
import en from "../../locales/en-US/conciergerie.json";
import Icon1 from "/public/1.svg";
import Icon2 from "/public/2.svg";
import Icon3 from "/public/3.svg";
const Conciergerie = () => {
let { locale } = useRouter();
let t = locale === "es-ES" ? es : en;
// const { t } = useTranslation(locale, "conciergerie");
let myIcons = [Icon1, Icon2, Icon3];
const scrollToConciergerie = () => {
window.scrollTo({
top: 300,
behavior: "smooth",
});
};
const myLoader = ({ src, width, quality }) => {
return `${src}?w=${width}&q=${quality || 75}`;
};
const [showform, setshowform] = useState(false);
useEffect(() => {
window.addEventListener("load", scrollToConciergerie);
return () => {
window.removeEventListener("load", scrollToConciergerie);
};
});
const showContactForm = () => {
return <Contact />;
};
const contentData = t.conciergerieData;
return (
<div className="section" onLoad={scrollToConciergerie}>
<div className="container">
<div className="text-center">
<h1 className=" my-4 text-capitalize" id="conciergerie">
{t.conciergerieHeader}
</h1>
</div>
<h3 className="text-capitalize concierge-subheading mt-3">
{t.conciergerieTitle}
</h3>
<p className="lead concierge-subheading-text">{t.conciergerieText}</p>
</div>
<div className="container">
<div className="row text-center mt-5">
{contentData?.map((item, index) => {
return (
<div className="col-md-4" key={index}>
<span className="fa-stack fa-4x">
<Image
layout="responsive"
src={myIcons[index]}
alt="icons"
className="svg-inline--fa fa-solid fa-stack-1x fa-inverse img-fluid"
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="house"
role="img"
objectFit="cover"
height={300}
width={300}
//loader={myLoader}
/>
</span>
<h4 className="my-3 text-hogar2 text-uppercase">
{item.title}
</h4>
<ul>
{item.text.map((text) => {
return (
<li key={text.id} className="list-unstyled">
<p className="m-0 text-muted text-list">
{text.content}
</p>
</li>
);
})}
</ul>
{item.id === "algomas" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
{item.id === "else" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
</div>
);
})}
</div>
{showform && showContactForm()}
</div>
</div>
);
};
export default Conciergerie;
can someone help me please?
The reason this problem is happening has to do with absolute and relative paths.
fetch("api/contact")
Is a relative path. The fetch function figures out the path of the current file, ie 3000/ourServices/conciergerie, and adds api/contact to it
On the other hand, if you add a "/" before the path :
fetch("/api/contact")
Fetch figures out the root path of the project, then adds the path you added, ie :
3000/api/contact
TL;DR: Change fetch("api/contact") to fetch("/api/contact").

How can I create Single Page

How can I pass map items (title, category and images) in my id.jsx file.
Basically, I just want to create a single page for my projects. But I can only access post ID. I don't know how to pass other data items.
'Projects folder'
[id].js
import { useRouter } from "next/router";
const Details = () => {
const router = useRouter();
return <div>Post #{router.query.id}
// Single Project Title = {project.title} (like this)
</div>;
};
export default Details;
index.js
import { MyProjects } from "./MyProjects";
const Projects = () => {
const [projects, setProjects] = useState(MyProjects);
{projects.map((project) => (
<Link
href={"/projects/" + project.id}
key={project.id}
passHref={true}
>
<div className="project__item">
<div className="project__image">
<Image src={project.image} alt="project" />
</div>
<div className="project_info">
<h5>{project.category}</h5>
<h3>{project.title}</h3>
</div>
</div>
</Link>
))}
If I understand your question correctly, you want to send some "state" along with the route transition. This can be accomplished using an href object with the "state" on the query property, and the as prop to hide the query string.
Example:
{projects.map((project) => (
<Link
key={project.id}
href={{
pathname: "/projects/" + project.id,
query: {
id: project.id,
category: project.category,
title: project.title
}
}}
passHref={true}
as={"/projects/" + project.id}
>
<div className="project__item">
<div className="project__image">
<Image src={project.image} alt="project" />
</div>
<div className="project_info">
<h5>{project.category}</h5>
<h3>{project.title}</h3>
</div>
</div>
</Link>
))}
...
const Details = () => {
const router = useRouter();
return (
<div>
<div>Post #{router.query.id}</div>
<div>Title {router.query.title}</div>
<div>Category {router.query.category}</div>
</div>
);
};

How to pass data from one component to another in React [duplicate]

This question already has answers here:
How to pass data from child component to its parent in ReactJS?
(18 answers)
Closed 1 year ago.
I want to send variable data Store component to Related Component, I am getting Value const catId=this.state.data.category_id; now i am sending via props to Related Component but I dont know how to use props in Realted Component, i want to assign inside of didmonunt function for example
const url = "https://localhost:3030/api/v5/web/related";
const postBody = {
category_id: catId,
here my code:
Store.js
import React, { Component } from "react";
// import ReactDOM from "react-dom";
import { Helmet } from "react-helmet";
import { Grid, Image, Icon, Segment, Card } from "semantic-ui-react";
import TopMenuStrip from "../ComponentList/TopMenuStrip";
import LogoSection from "../ComponentList/LogoSection";
import { withRouter } from "react-router-dom";
import OfferList from "./OfferList";
import Related from "./Related";
import CopyRight from "../ComponentList/CopyRight";
import Footer from "../ComponentList/Footer";
import CustomerReview from "./CustomerReview";
class Store extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true,
};
}
async componentDidMount() {
console.log(this.props.match.params.id);
let url_id = this.props.match.params.id;
const url = "https://localhost:3030/api/v5/web/details";
const postBody = {
store_id: url_id,
offer_type: "",
};
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postBody),
};
fetch(url, requestOptions)
.then((response) => response.json())
.then((json) => {
this.setState({ data: json });
//console.log(json);
// console.log(json);
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
render() {
var i;
// console.log(this.props)
if (!this.state.data.how_to) {
return null;
}
if (!this.state.data.store_terms) {
return null;
}
const catId=this.state.data.category_id;
const seokeyWord = this.state.data.seo_keywords;
const seoDescription = this.state.data.seo_description;
return (
<>
<Helmet>
<meta charSet="utf-8" />
<title>test</title>
<meta name="description" content={seokeyWord} />
<meta name="keywords" content={seoDescription} />
<meta name="google-site-verification" content="rtIRrUNRpgZ_lFtlfS8LJ0-8j_d569BXS9NOGa_nM6Y" />
</Helmet>
<TopMenuStrip />
<LogoSection />
<div>
<Grid className="storedetails">
<Grid.Column width={11}>
<div className="storeImage">
<div className="store-bg">
<h2>
<span>{this.state.data.store_name}</span>
<span className="store-right">
{
(i =
0 < this.state.data.rating ? (
<p>
<span className="rateNumber">
{this.state.data.rating}
</span>
<Icon id={i} className="star"></Icon>
</p>
) : (
<p className="no-reviews">No Reviews</p>
))
}
</span>
</h2>
<p className="">{this.state.data.summary} </p>
{/* <p><span className='rateNumber'>{this.state.data.rating}</span><Icon className='star'/><Icon className='star'/><Icon className='star'/><Icon className='star'/><Icon className='star'/><span className='totalRate'>39000</span></p> */}
<div
className="store-back"
Style={
"background: url(" + this.state.data.store_images + ")"
}
>
{/* <Image src= alt="" className='storeImage'/> */}
<Image
src={this.state.data.logo}
alt=""
className="storeImageLogo"
/>
</div>
</div>
<div className="related-section">
<OfferList />
</div>
</div>
</Grid.Column>
<Grid.Column className="four-1" width={5}>
<div className="storeAbout" centered>
<h3>About</h3>
<p
dangerouslySetInnerHTML={{
__html: this.state.data.description,
}}
></p>
</div>
<CustomerReview />
<Related CatID={catId}/>
</Grid.Column>
</Grid>
<Grid className="related-footer">
<Grid.Column width={16}>
</Grid.Column>
</Grid>
</div>
<Footer />
<CopyRight />
</>
);
}
}
export default withRouter(Store);
Related.js
import React, { Component } from 'react';
// import ReactDOM from "react-dom";
import { Grid, Icon, Segment, Card } from "semantic-ui-react";
import { Link } from "react-router-dom";
import { LazyLoadImage } from 'react-lazy-load-image-component';
class Related extends Component{
constructor(props) {
super(props);
this.state = {
data: [],
visible: 6,
isLoading: true,
dataLoaded: false,
rec:CatID,
};
this.loadMore = this.loadMore.bind(this);
}
loadMore() {
this.setState({
visible: this.state.visible + 2,
dataLoaded: this.state.visible >= this.state.data.length
});
}
async componentDidMount() {
// console.log(this.props.match.params.id);
const url = "https://localhost:3030/api/v5/web/related";
const postBody = {
category_id: CatID,
offer_type: "",
};
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postBody),
};
fetch(url, requestOptions)
.then((response) => response.json())
.then((json) => {
this.setState({ data: json.stores });
// console.log(json);
// console.log(json);
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
render()
{
return (
<>
<div className="related-stores" centered>
<h3 className="realted-heading">Related Stores </h3>
<Grid className="">
{this.state.data.slice(0, this.state.visible).map((x, i) => {
return (
<Grid.Column columns={8} key={i}>
<Segment>
<Card>
<Link to={x.store_url}>
<div className="image">
<LazyLoadImage src={x.image} wrapped ui={false} className="img related"/>
</div>
</Link>
<LazyLoadImage src={x.logo} alt="" className="collection-logo" />
<Card.Content>
<Link to={x.store_url}>
{" "}
<Card.Header>{x.name}</Card.Header>
</Link>
<Card.Description>{x.store_summary}</Card.Description>
</Card.Content>
<Card.Content extra>
<p className="rewards">
<span>
<LazyLoadImage src="/images/rewards.png" alt="" className="img"/>
</span>
Cash rewards upto <span>AED {x.cashback}</span>
</p>
<p className="location">
<span>
<LazyLoadImage src="/images/location.png" alt="" className="img"/>
</span>
<span className="store-location" key="index">{x.store_branches}</span>
{/* {x.store_branches.map((locations, index) => (
<span className="store-location" key="index">
{index === 0 ? (
<span>{locations.store_location}</span>
) : index >= 1 ? (
<span>
, {locations.store_location}
</span>
) : null}
</span>
))} */}
</p>
</Card.Content>
</Card>
</Segment>
</Grid.Column>
);
})}
</Grid>
{
!this.state.dataLoaded && (
<Grid>
<Grid.Column width={16}>
<p className="related-load">
<span
onClick={this.loadMore}
className="btn btn-primary load-more"
Style="cursor:pointer;">
Loading Store
</span>
</p>
</Grid.Column>
</Grid>
)
}
</div>
</>
)
}
}
export default Related;
In the Store component, pass storeCategory as props to the Related component.
In the Related component, access categoryId from the props and set it as category_id in postBody.
class Related extends Component {
...
async componentDidMount() {
...
const postBody = {
category_id: this.props.categoryId,
...
}
...
}
class Store extends Component {
...
render() {
...
return(
...
<Related categoryId={storeCategory} />
...
);
}
}
Explanation:
Related is a child of Store (parent).
To pass data from a parent component to a child component, use props.
Set an attribute on , e.g. categoryId, and assign the data (storeCategory) to it:
<Related categoryId={storeCategory} />
Store will then receive categoryId among its props. In Store, you can thus access categoryId from the props:
this.props.categoryId
Further reading:
Components and props

Resources