How can I create Single Page - reactjs

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>
);
};

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 fetch unique item from redux with conditional operator?

I am trying to fetch only unique data from the redux store but got all values from the redux store.
I use usePerma hook to check id from URL and id of redux data then apply ternary operator but did not get desired output. See the output image and code and please tell how to get only one value from that.
When URL is 1002 only 1002 is item should show but its also showing 1001 item.
Here is my code:
import React from 'react'
import { NavLink } from 'react-router-dom';
import { useStateValue } from './Stateprovider';
import { useParams } from 'react-router';
const User = () => {
const [{basket}, dispatch] = useStateValue();
const {id} = useParams();
return (
<>
{basket.map((item) => (
<>
{
({id} === {item.id}) ? //Error
<div key="id" className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home' > User Info </NavLink>
</div>
: null
}
</>
))}
</>
)
}
export default User;
If I understand your question correctly, you want to map only the items from the basket array that match the id match param. For this you can either use Array.prototype.find to first find the matching item and conditionally render if found, or use Array.prototype.filter and just filter the array inline.
Filtering the array inline:
const User = () => {
const [{basket}, dispatch] = useStateValue();
const { id } = useParams();
return (
<>
{basket
.filter(item => item.id === id)
.map((item) => (
<div key={item.id} className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home'>
User Info
</NavLink>
</div>
))
}
</>
)
}
Finding first, then rendering: remember that .find returns undefined if no match is found
const User = () => {
const [{basket}, dispatch] = useStateValue();
const { id } = useParams();
const item = basket.find(item => item.id === id);
return item ? (
<div className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home'>
User Info
</NavLink>
</div>
) : null;
}

react js i am trying to make a pagination

i am trying to make a pagination and filter based on experience i want max only 10 items in a page
i dont know how to get stated i have all the days dispayed and all day is now dispalyed in single page i want only 10 items in 1 page and next 10 items in next page
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import DocPic from "../img/doctor.png";
import InnerDetail from "./innerto_detail";
import axios from "axios";
export default function InnerSpecialities() {
const [hasId, setHasId] = useState(false);
const [detail, setDetail] = useState(false);
const [data, setdata] = useState([]);
const inipath = window.location.pathname;
const id_path = inipath.split("/cdoctor/details");
const path = inipath.split("/cdoctor/");
useEffect(() => {
const pathname = window.location.pathname;
const doctorId = pathname.split("/")[2];
console.log("this is the ", doctorId);
console.log(id_path);
const substring = "detiled";
console.log(inipath.includes(substring));
if (inipath.includes(substring) === true) {
setDetail(true);
}
const config = {
headers: {
Authorization: `token ` + localStorage.getItem("token"),
},
};
axios
.get(
"filter/?speciality=" +
path[1],
config
)
.then((res) => {
var somevariable = res;
setdata((data) => {
return [...data, somevariable];
});
setdata(res.data);
});
}, []);
function OpenInner() {
setDetail(true);
}
return (
<>
{detail ? (
<InnerDetail />
) : (
<div>
<div>
<div className="list-container" style={styles.options}>
<div style={styles.inneropt}>
<i style={styles.rotate} class="fa fa-exchange"></i>Sort By:
<select style={styles.sel}>
<option> Availability</option>
<option> Nearby</option>
<option> Price- Low to high</option>
<option> Price- High to low</option>
</select>{" "}
</div>
<div style={styles.inneropt}>
Gender:
<select style={styles.sel}>
<option> Male</option>
<option> Female</option>
</select>{" "}
</div>
<div style={styles.inneropt}>
Experience:
<select style={styles.sel}>
<option> 0-5</option>
<option> 5-10</option>
<option> 10-15</option>
<option> 15+ </option>
</select>{" "}
</div>
<div className="splang" style={styles.inneropt}>
Language:
<select style={styles.sel}>
<option> English</option>
<option> Malayaliam</option>
<option> Tamil</option>
<option> Kannada</option>
</select>{" "}
</div>
</div>
<div style={styles.container}>
{data.map((personData, key) => {
return (
<div style={styles.fbox}>
<div style={styles.fitem}>
<left>
<img src={DocPic} alt="" />
<br />
<i className="fa fa-video-camera"></i>
<br />
<small>Online</small>
</left>
<right style={{ textAlign: "left", marginLeft: "6px" }}>
<strong>
Dr {personData.firstname}
{personData.lastname}
</strong>
<br />
<small>
{" "}
{personData.speciality} |{personData.experience}
<small> years Exp.</small>
</small>
<br />
<strong>You Pay</strong>
<br />
<strong>$600</strong>
<br />
<small>
{personData.qualification}
{personData.location}
</small>
</right>
</div>
<Link
to={{
pathname: "/doctor/detiled/" + personData.id,
state: { id: personData.id, data: personData },
}}
onClick={OpenInner}
>
<button style={styles.book}>Book Appointments</button>
</Link>
</div>
);
})}
</div>
<div style={styles.pagination} className="pagination">
<div className="pager" style={{ margin: "auto", width: "30rem" }}>
<Link to="">
{" "}
<i className="fa fa-angle-left"></i>{" "}
</Link>
<Link to="">1</Link>
<Link to="">2</Link>
<Link to="">3</Link>
<Link to="">4</Link>
<Link to="">5</Link>
<Link to="">6</Link>
<Link to="">7</Link>
<Link to="">
{" "}
<i className="fa fa-angle-right"></i>{" "}
</Link>
</div>
</div>
</div>
</div>
)}
</>
);
}
i am trying to make a pagination and filter based on experience i want max only 10 items in a page i dont know how to get stated i have all the days dispayed and all day is now dispalyed in single page i want only 10 items in 1 page and next 10 items in next page
Have state variables for loading and the page count,
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [dataPerPage, setDataPerPage] = useState(10); // 10 items needed per page
Your API call response should set the loading status after the setData()
useEffect(()=>{
function getData(){
setLoading(true); // until the response is available, user will see loader
axios
.get(
"filter/?speciality=" +
path[1],
config
)
.then((res) => {
var somevariable = res;
setdata(res.data);
setLoading(false);
}).catch(....)
}
getData();
},[])
Here is the pagination logic,
const indexOfLastdata = currentPage * dataPerPage; // 1 * 10
const indexOfFirstData = indexOfLastdata - dataPerPage;
const currentData = posts.slice(indexOfFirstData, indexOfLastData);
First page index will be index of last data minus the number of data you want to allow per page, when this is zero, its first page, if its 10 then its second page and so on. You'll control this through the current page number on click of next/number of the page.
Example pagination component below will take total data available, the posts allowed per page and paginate function as props.
const Pagination = ({dataPerPage, totalData, paginate}) => {
const pageNumbers = [];
for(let i=1; i<= Math.ceil(totaldata/dataPerPage); i++){
pageNumbers.push(i);
}
return (
<nav>
<ul className="ur style class for nav">
{pageNumbers.map(number=>{
return <li key={number}>
<a onClick={ () => paginate(number) } className={currentPage===number ? "active": ""}>
{number}
</a>
</li>
})}
</ul>
</nav>
);
};
In your component,
return (
<div>
<Data data={currentData} loading={loading}/>
<Pagination dataPerPage={dataPerPage} totalData={data.length} paginate={handlePaginate}/>
</div>
)
I have created a codesandbox with a working example for you here.
https://codesandbox.io/s/mutable-water-msoy9
You need to set the pageCount and pageSize variables. Page size would be 10 in your case. Based on the current current page index, you need to extract that part of your data array and render on that page.

separate a page to components in react

this is one of my pages in react app and it shows everything ok:
const showAllFavorite = (props) => {
const [favorites, setFavorites] = useState([]);
useEffect(async () => {
const resp = await generalGet({}, '/favorite');
setFavorites(resp.data);
}, [])
return(
<React.Fragment>
<MainMenu title="favorites" history={props.history}/>
<div className="container mt-4">
<div className="d-flex flex-wrap">
{
favorites.map(fav => (
<div style={{flex: '50%'}}>
<Link
to={`/recipe/${fav.id}`}
key={fav.id}
>
<FoodItem
className="foodItem"
name={fav.name}
cover={fav.cover}
width={'auto'}
/>
</Link>
</div>
))
}
</div>
</div>
</React.Fragment>
)
}
export default showAllFavorite;
but when I want to code like this it doesn't show anything :
const showAllFavorite = (props) => {
const [favorites, setFavorites] = useState([]);
useEffect(async () => {
const resp = await generalGet({}, '/favorite');
setFavorites(resp.data);
}, [])
return(
<showFoodItems title={"favorite"} items={favorites} history={props.history} />
)
}
export default showAllFavorite;
and my showFoodItem component:
const showFoodItems= ({title , items , history}) => {
return(
<React.Fragment>
<MainMenu title={title} history={history}/>
<div className="container mt-4">
<div className="d-flex flex-wrap">
{
items.map(item => (
<div style={{flex: '50%'}}>
<Link
to={`/recipe/${item.id}`}
key={item.id}
>
<FoodItem
className="foodItem"
name={item.name}
cover={item.cover}
width={'auto'}
/>
</Link>
</div>
))
}
</div>
</div>
</React.Fragment>
)
}
export default showFoodItems;
I want to do this because I want to use the showFoodItems component many times in my app.
I checked the network tab and data is receiving from server. do you have any idea?
The problem is most likely with your component names.
React treats components that start with lower case letters as DOM tags so changing the names to ShowAllFavorite and ShowFoodItems should fix your issue.
You can read more about the reasoning behind this in React's documents here.

Nextjs - getInitialProps make reload and delete store

I built a site with nextjs (with server express & API) and Reactjs.
I would like to create dynamic paginations because there is far too much result for statically generated, So I added server endpoint /publiations/page/:id, I put a getInitialsProps for keep the id in query
But actually, when I click on my main page /publications where my store is not empty to go to the next page (publications/page/1), the page reloads and the store is empty. How I can keep my store when I change route?
And here my publications/page/[id].js
const PublicationsPage = ({id}) => {
return (
<>
<MainMenu/>
<Search/>
<div className="flex">
<Sidebar fallback={<Loader/>}/>
<Cards type={'publications'} idPage={id} />
</div>
</>
)
}
PublicationsPage.getInitialProps = async function({ query: { id } }) {
return {
id: id
};
};
export default withAuthSync(PublicationsPage);
The cards components where i use the data of store :
components/Cards.js
const Cards = ({ idPage, cards, type }) => {
console.log(cards)
return (
<div className="cards">
<div className="content-filter-search">
<div className="content-newsearchresult">
<div className="global-name">Result: {cards.cards.length} articles found</div>
<div className="content-button-blue">
<Link href="/explorer">
<a>
<div className="button-blue">New search</div>
</a>
</Link>
</div>
</div>
<div className="content-filter">
{filters[idPage].map((item) => {
return <InputFilter key={item.id} data={item} callback={keepItems} />;
})}
</div>
</div>
<div className="wrapper-card">
<div className="cards-container">
{
!cards.loading ? cards.cards.slice(idPage * 9, idPage * 9 + 9).map((card) => (
<Card key={card.PMCID} data={card} />
)) : <Spinner color="black" size="100px" thickness={3} gap={5} speed="fast" />
}
</div>
</div>
<div className="text">
<Link href={'/publications/page/1'}><a>Next page</a></Link>
</div>
{
!cards.loading ? (
<div className="center">
<Pagination type={type} page={parseInt(idPage)} totalElement={cards.cards.length} numberPerPage={9} />
</div>
) : null
}
</div>
);
};
const mapStateToProps = state => ({
cards: state.cards,
})
export default connect(mapStateToProps)(Cards);
I use the same code for the route /publications and /publications/page/:id just I add the getInitialProps to keep the id of page. And I use connect redux in my Cards component
I have no error in the console just my store is reset because the page reload. I don't understand how I can make pagination with my store where is my data if when I change page the store is empty
Thanks

Resources