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.
Related
In reactjs, what code would I have to write in my two components(homepage.js with an add-to-cart button and a checkout page with a clear cart button? This is my Homepage.js where I fetch the items from my backend server, map, and display them on the page.
function Home() {
const [data, setData] = useState([]);
useEffect(() => {
// auto-login
fetch("/coffees").then((res) => {
if (res.ok) {
res.json().then((data) => setData(data))
}
});
}, [])
console.log(data)
return (
<>
<div className="box">
<div className="projo">
<h2 >Welcome to Coffee-cafe</h2>
</div>
<div className="projects">
{data.map((data) =>{
return(
<div className="card">
<img className="avatar" src={data.coffee_image} alt="coffee"></img>
<div className="card-content">
<h4><b>{data.coffee_name}</b></h4>
<p id="desc">{data.description}</p>
<p className="price">{data.price}</p>
{/* <p>{data.category}</p> */}
<button onClick={() => ({})} className="btn1" id="btn1">Add to Cart</button>
</div>
</div>
)
})}
</div>
</div>
</>
)
}
export default Home;
For the Checkout.js, this is what I have currently.
function Checkout() {
return (
<>
<div className="section">
<div className="photo">
<img className="dp" src={image1} alt="coffee"></img>
<div className="info">
<h3>Coffee:</h3>
<p>Price:</p>
<p>Category:</p>
<div className="check-out-btn">
{/* <button className="add">AddToCart</button> */}
<button className="delete">ClearCart</button>
</div>
</div>
</div>
</div>
take a look for a global states like redux , context,or zustand
for my recommendation, I suggest zustand is less code than context and redux and short learning curve
carts =[]
setItemToCart(item){
const isExist = carts.find(c => c.id ===item.id)
if(isExit){
const qty = isExit.qty+item.qty
const newItem= {...item,qty}
// push is not recommended to use because it mutates state
carts.push(newItem)
}else{
carts.push(item)
}
}
I'm trying to build a page that paginates all the data from a collection, however I can't seem to figure out how to do it and I haven't found anyone who's done it either.
I've tried to get the documentList.length, but that also isn't working.
function Sessions() {
const location= useLocation();
const user= location.state.user;
const [sesList, setSesList] = useState([]);
const subRef=collection(db, 'Users',user,'Sessions');
let q = query(subRef,orderBy('time','desc'),limit(5));
const DocumentSnapshot=getDocs(q);
const last= DocumentSnapshot[DocumentSnapshot.length-1]
const next=()=>{
return q=query(subRef,orderBy('time','desc'), startAfter(last), limit(5))
}
const prev=(first)=>{
return q=query(subRef, orderBy('time','desc'), endBefore(first['time','desc']), limitToLast(5))
}
useEffect(() => { //loads all the tenants
onSnapshot(q, (snapshot) => {
setSesList(
snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
);
});
}, []);
return (
<div className='Sessions'>
<Sidebar className='nav'
L1={<Link to='/Dashboard' state={{user:user}}>Dashboard</Link>}
L2={<Link to='/Sessions' state={{user:user}}>Sessions</Link>}/>
<div className="bod">
<div className="startCard1">
<h1>Sessions</h1>
</div>
<div className="Recent">
<h1>Your Sessions:</h1>
<div className="recSes">
{sesList.map((REC)=>{
return(
<div className="rowCard">
<div className="rowCardTitle">
<h3> Subject: {REC.subject}</h3>
</div>
<div className="WTime">
<h3>Work Minutes: {REC.WorkTime}</h3>
</div>
<div className="BTime">
<h3> Break Minutes: {REC.BreakTime}</h3>
</div>
<div className="Atime">
<h3>Date: {REC.time}</h3>
</div>
</div>
)
})}
</div>
</div>
<div className="buttoninlin">
<button className='Gobtn1'>Previous </button>
<button className='Gobtn1' onClick={next}>Next</button>
</div>
</div>
</div>
)
}
export default Sessions
expected result: next button shows next documents, back button shows previous documents
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>
);
};
I want refresh page and send data using link but it don't work
I am using a link to send me to same page but with different parameters but it is not work and it is not refresh ... what I should do?
When I click the link it change from link but it is not change in the page and does not refresh with the data does not change. What should I do?
<Link to={`/NewDetails/${news[arr].id}`}> read more </Link>
the js I'm using is
const NewDetails = () => {
const { id } = useParams();
const [newsDetail, setNewsDetail] = useState("");
useEffect(() => {
async function fetchMyAPI() {
let news = await onGetNewsById(id);
setNewsDetail(news);
}
fetchMyAPI();
}, []);
const [news, setNews] = useState("");
useEffect(() => {
async function fetchMyAPI() {
let news = await onGetNews();
setNews(news);
}
fetchMyAPI();
}, []);
const last_num = news.length;
const last_3_num = news.length - 3;
return (
<div>
<div>
<NavBar />
</div>
<div className="row">
<div className="NewsDetails_Section">
<div className="text-center">
<div className="imageDetailsBackground">
<img className="ImageNewsDetails" src={Wallet} alt="bankimage" />
</div>
</div>
<div className="text-center" dir='rtl'>
<h1> {newsDetail.title} </h1>
</div>
<div className="Body_news_Details">
<p className="text-center"> {newsDetail.content} </p>
</div>
<div>
<div className="News_Details_" dir="rtl">
{Object.keys(news.slice(last_3_num, last_num), NewsImage.slice(last_3_num, last_num)).map(arr => (
<div className="NewSection_details" key={news[arr].id}>
<div className="ImageNews_Details">
<img className="ImageNews_Details" src={NewsImage[arr]} alt="bankimage" />
</div>
<div className="HeaderNews_Details">
<h4>{news[arr].title} </h4>
</div>
<div className="bodynews_details">
<p> {news[arr].content.slice(0, 100)}... </p>
</div>
<div className="ButtonNews_Details">
{/*
<Link Redirect push to={`/NewDetails/${news[arr].id}`}> */}
<Link to={`/NewDetails/${news[arr].id}`}> read more</Link>
</div>
</div>))}
</div>
</div>
</div>
</div>
</div>
);
}
I sat a counter which is counting the total number of visit on my post instead of counting the views on per post. here is the code below:
const ViewPost=()=>{
const [user,setUser] = useState({
title:"",
body:"",
date:""
});
const {id} = useParams();
const [count,setCount]=useState(parseInt(localStorage.getItem('count')));
console.log(count)
useEffect(()=>{
loadUser();
},[])
const IncNum=()=>{
setCount(count+1);
}
localStorage.setItem('count',count)
const loadUser = async ()=>{
const result = await axios.get(`http://localhost:3004/posts/${id}`,IncNum());
setUser(result.data);
// console.log(result.data)
} ;
return(
<div>
<span> {(user.title) ==='' ?<ErrorPage /> :
<div className="container py-4">
<Link className="btn btn-primary" to= "/blog"> Back to home</Link>
<h1 className="display-4"> Post: {id}</h1>
<hr/>
<ul className="list-group w-100">
<li className="list-group-item"> Title:{user.title}</li>
<li className="list-group-item"> Body:{renderHtml(user.body)}</li>
<li className="list-group-item"> Date:{user.date}</li>
</ul>
<h1> {/*count*/} </h1>
</div>
}</span>
</div>
)
}
export default ViewPost;