react js i am trying to make a pagination - reactjs

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.

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 get a particular Index from a shuffled array

I've been working on a quiz app using NextJs and after figuring out how to work around the hydration issue in Nextjs, Now I'm stuck trying to figure out how to keep track of a value in the array.
export default function quiz({ questions }) {
const link = `https://the-trivia-api.com/api/questions`;
const [number, setNumber] = useState(0);
const [quizData, setQuizData] = useState(questions);
const [numbering, setNumbering] = useState(1);
const [initialRenderComplete, setInitialRenderComplete] = useState(false);
console.log(quizData[number].correctAnswer);
const oldArray = [...quizData[number].incorrectAnswers, quizData[number].correctAnswer];
oldArray.push(quizData[number].correctAnswer); //Here I pushed the correct answer and incorrect answer into a single array.
const newArray = [...new Set(oldArray)]; // 'newArray' is the new array containing all the four options.
const increase = () => {
setNumber(number + 1);
setNumbering(numbering + 1);
};
const decrease = () => {
setNumber(number - 1);
setNumbering(numbering - 1);
};
useEffect(() => {
setInitialRenderComplete(true);
}, []);
if (!initialRenderComplete) {
return null;
} else {
newArray.sort(() => (Math.random() > 0.5 ? 1 : -1)); // Here i shuffled the newArray, So the correct and incorrect answer is shuffled.
return (
<div className={styles.container}>
<div className={styles.profile}>
<div className={styles.mainProfile}>
<div className={styles.profilePic}>
<img src={image} alt="img" />
</div>
<h2>{state} </h2>
</div>
</div>
<div className={styles.main}>
<h2>
{numbering}. {questions[number].question}{" "}
</h2>
<div className={styles.list}>
<ul className={styles.list1}>
<div className={styles.flex}>
<h3>A. </h3>
<button>
<li>{newArray[1]} </li>
</button>
</div>
<div className={styles.flex}>
<h3>B. </h3>
<button>
{" "}
<li> {newArray[0]} </li>
</button>
</div>
</ul>
<ul className={styles.list2}>
<div className={styles.flexOption}>
<h2>C. </h2>
<button>
<li>{newArray[3]}</li>
</button>
</div>
<div className={styles.flexOption}>
<h2>D. </h2>
<button>
<li>{newArray[2]} </li>
</button>
</div>
</ul>
</div>
<div className={styles.btnStyle}>
<button onClick={decrease} className={styles.prev}>
Previous{" "}
</button>
<button onClick={increase} className={styles.next}>
Next{" "}
</button>
</div>
</div>
</div>
);
}
}
//Here is my Api Call
export const getStaticProps = async () => {
const data = await Axios.get("https://the-trivia-api.com/api/questions");
// const data = req;
const initialData = data.data;
return {
props: {
// question: data.data[indexing],
questions: initialData,
},
};
};
Now I'm stuck on how to keep track of which is the correct or incorrect Option onClick of the button, I figured I could maybe keep track of the index of the correct answer in the array but I don't think it would work and I can't figure out how to do it, Thanks in Advance.

How to transfer product items to the payment page after clicking the checkout button in React? [duplicate]

This question already has an answer here:
How do you pass data when using the navigate function in react router v6
(1 answer)
Closed 5 months ago.
Goal: I should transfer all the added product items from the product cart to the payment page after the user will click the checkout button.
Product items inside the product cart:
I should transfer the product items here in the Payment page (after checkout button is clicked):
Problem: I am not sure how to transfer the product items(including its prices, total prices, and increment/decrement buttons) to the payment page, even by using props and replicating mapping functionality of the Basket.jsx(where the cart functionality is found).
I know I shouldn't be replicating the functionality, especially in terms with mapping since there would be one and only one parent component for this.
Source code for Basket.jsx:
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route,
useNavigate
} from "react-router-dom";
export default function Basket(props) {
const navigate = useNavigate();
const navigateToPaymentPage = () => {
navigate("/paymentpage");
};
const { cartItems, onAdd, onRemove } = props;
const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0);
const totalPrice = itemsPrice;
// const totalPrice = itemsPrice + discountItemPrice ---- for discount items soon
return (
<aside className="block col-1">
<h2>Cart Items</h2>
{/* Display message when cartItemsLength is 0 */}
<div>{cartItems.length === 0 && <div>Cart is Empty</div>} </div>
{/* Renders the added item to the basket of the shopping cart through mapping cartItems */}
{cartItems.map((item) => (
<div key={item.id} className="row">
<div className="col-2">
{item.name} -- ${item.price.toFixed(2)}
</div>
{/* Increment and Decrement Buttons */}
<div className="col-2">
<button onClick={() => onRemove(item)} className="remove">
-
</button>
<button onClick={() => onAdd(item)} className="add">
+
</button>
Qty: {item.qty}
</div>
<div className="col-2 text-right">
${(item.price * item.qty).toFixed(2)}
</div>
</div>
))}
{cartItems.length !== 0 && (
<>
<hr></hr>
<div className="row">
<div className="col-2">
<strong>Total Price</strong>
</div>
<div className="col-1 text-right">
<strong>${totalPrice.toFixed(2)}</strong>
</div>
</div>
<hr />
<div className="row">
<button onClick={navigateToPaymentPage}>Checkout</button>
</div>
</>
)}
</aside>
);
}
Source code for PaymentPage.jsx:
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route,
useNavigate
} from "react-router-dom";
export default function PaymentPage(props) {
//I replicated the functionality here:
const { cartItems, onAdd, onRemove } = props;
const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0);
const totalPrice = itemsPrice;
const navigate = useNavigate();
const navigateToHomeOrderPage = () => {
navigate("/");
};
return (
<aside className="block col-1">
<button
sx={{ width: 10 }}
style={{ maxWidth: "60px" }}
onClick={navigateToHomeOrderPage}
>
Go back
</button>
<h2>PAYMENT PAGE</h2>
{/* Display message when cartItemsLength is 0 */}
<div>{cartItems.length === 0 && <div>Cart is Empty</div>} </div>
{/* Renders the added item to the basket of the shopping cart through mapping cartItems */}
{cartItems.map((item) => (
<div key={item.id} className="row">
<div className="col-2">
{item.name} -- ${item.price.toFixed(2)}
</div>
{/* Increment and Decrement Buttons */}
<div className="col-2">
<button onClick={() => onRemove(item)} className="remove">
-
</button>
<button onClick={() => onAdd(item)} className="add">
+
</button>
Qty: {item.qty}
</div>
<div className="col-2 text-right">
${(item.price * item.qty).toFixed(2)}
</div>
</div>
))}
{cartItems.length !== 0 && (
<>
<hr></hr>
<div className="row">
<div className="col-2">
<strong>Total Price</strong>
</div>
<div className="col-1 text-right">
<strong>${totalPrice.toFixed(2)}</strong>
</div>
</div>
<hr />
</>
)}
</aside>
);
}
Full source codes (functioning App):
https://codesandbox.io/s/productitemsdisplayfixing-cherry-h0br4o-forked-nvu3d0?file=/src/components/PaymentPage.jsx:0-1954
Code Basis: https://www.youtube.com/watch?v=AmIdY1Eb8tY&t=2209s
Your responses would indeed help and guide me a lot since I am very much confused on how to pass these data from one page component to another. Thus, it would be great to hear guides and responses from all of you. Thank you very much!
Just as in the linked answer in the comments, you can pass the data in the state option of the navigate function
const navigate = useNavigate();
const { cartItems, onAdd, onRemove } = props;
const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0);
const totalPrice = itemsPrice;
const navigateToPaymentPage = () => {
navigate("/paymentpage", {
state: {
totalPrice,
cartItems
}
});
};
And then get it on the other page from the location object
const navigate = useNavigate();
const location = useLocation();
const navigateToHomeOrderPage = () => {
navigate("/");
};
const data = location.state;
return (
<aside className="block col-1">
<button
sx={{ width: 10 }}
style={{ maxWidth: "60px" }}
onClick={navigateToHomeOrderPage}
>
Go back
</button>
<h2>PAYMENT PAGE</h2>
{JSON.stringify(data)}
</aside>
);
You can check the updated sandbox https://codesandbox.io/s/react-router-dom-pass-state-r1iis7?file=/src/components/Basket.jsx
However, a cart implementation usually uses a backend as a source of truth, but if your project is for learning purposes, I recommend you to also take a look at global state management libraries, the most common is reduxjs in case you need to persist the data through route changes or even between page reloads if you add redux-persist

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

Use State not updating as expected

Fairly new to react and trying to build a clone of The Movie Database site. I want this toggle switch to change my api call from movies to tv. It starts working after a couple clicks, but then it throws everything off and it's not displaying the correct items anyway. Not really sure what's going on here...or even why it starts working after two clicks. Anyone know whats up with this?
import React, { useState, useEffect } from "react";
import axios from "axios";
import API_KEY from "../../config";
const Popular = ({ imageUri }) => {
// GET POPULAR MOVIES
const [popularMovies, setPopularMovies] = useState("");
const [genre, setGenre] = useState("movie");
console.log(genre);
const getPopular = async () => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${genre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, []);
const listOptions = document.querySelectorAll(".switch--option");
const background = document.querySelector(".background");
const changeOption = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
getPopular();
listOptions.forEach((option) => {
option.classList.remove("selected");
});
el = el.target.parentElement.parentElement;
let getStartingLeft = Math.floor(
listOptions[0].getBoundingClientRect().left
);
let getLeft = Math.floor(el.getBoundingClientRect().left);
let getWidth = Math.floor(el.getBoundingClientRect().width);
let leftPos = getLeft - getStartingLeft;
background.setAttribute(
"style",
`left: ${leftPos}px; width: ${getWidth}px`
);
el.classList.add("selected");
};
return (
<section className="container movie-list">
<div className="flex">
<div className="movie-list__header">
<h3>What's Popular</h3>
</div>
<div className="switch flex">
<div className="switch--option selected">
<h3>
<a
data-genre="movie"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
In Theaters
</a>
</h3>
<div className="background"></div>
</div>
<div className="switch--option">
<h3>
<a
data-genre="tv"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
On TV
</a>
</h3>
</div>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies &&
popularMovies.map((movie, idX) => (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + "w500" + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export default Popular;
You're using the array index as your key prop when you're mapping your array.
You should use an id that is specific to the data that you're rendering.
React uses the key prop to know which items have changed since the last render.
In your case you should use the movie id in your key prop instead of the array index.
popularMovies.map((movie) => (
<div key={movie.id} className="card">
<div className="image">
<img src={imageUri + 'w500' + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
));
Also
You're calling the api directly after setGenre. However state changes aren't immediate. So when you're making your api call you're still sending the last movie genre.
Two ways of fixing this:
You could call your function with the genre directly, and change your function so it handles this case:
getPopular('movie');
Or you could not call the function at all and add genre as a dependency of your useEffect. That way the useEffect will run each time the genre change.
useEffect(() => {
getPopular();
}, [genre]);
PS: You should consider splitting your code into more component and not interacting with the DOM directly.
To give you an idea of what it could look like, I refactored a bit, but more improvements could be made:
const Popular = ({ imageUri }) => {
const [popularMovies, setPopularMovies] = useState('');
const [genre, setGenre] = useState('movie');
const getPopular = async (movieGenre) => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${movieGenre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, [genre]);
const changeHandler = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
};
const isMovieSelected = genre === 'movie';
const isTvSelected = genre === 'tv';
return (
<section className="container movie-list">
<div className="flex">
<MovieHeader>What's Popular</MovieHeader>
<div className="switch flex">
<Toggle onChange={changeHandler} selected={isMovieSelected}>
In Theaters
</Toggle>
<Toggle onChange={changeHandler} selected={isTvSelected}>
On TV
</Toggle>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies.map((movie) => {
const { title, id, poster_path } = movie;
return (
<MovieItem
title={title}
imageUri={imageUri}
key={id}
poster_path={poster_path}
/>
);
})}
</div>
</div>
</div>
</section>
);
};
export default Popular;
const Toggle = (props) => {
const { children, onChange, selected } = props;
const className = selected ? 'switch--option selected' : 'switch--option';
return (
<div className={className}>
<h3>
<a
data-genre="movie"
onClick={onChange}
className="switch--anchor"
>
{children}
</a>
</h3>
<div className="background"></div>
</div>
);
};
const MovieHeader = (props) => {
const { children } = props;
return (
<div className="movie-list__header">
<h3>{children}</h3>
</div>
);
};
const MovieItem = (props) => {
const { title, imageUri, poster_path } = props;
return (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + 'w500' + poster_path} />
</div>
<p>{title}</p>
</div>
);
};

Resources