button only clicks once, at one div element - reactjs

this is the onclick function
export function changeColorButton() {
document.getElementById("friendDiv").style.background = "grey";
}
this is the output file. I want every button to be clickable and give background grey
{data.projects.map((project, key) => {
return (
<div id="friendDiv" className="relative">
<div key={key} className="flex flex-row items-center space-x-6 mb-6">
<img src={project.image} />
<div>
<h1 key={key} class=" text-xl font-bold">
{project.name}
</h1>
</div>
<button
className="absolute right-10 bg-bgButtonAddPeople p-2"
onClick={changeColorButton}
>
Legg til
</button>
</div>
</div>
);
});
}

You would just need to componentize the div element that is being mapped. So you can follow an approach like this.
Each FriendDiv element would have its own instance of changeColorButton, so it would apply the color to its own element.
FriendDiv.js
const FriendDiv = ({ key, project }) => {
const [isGrey, setIsGrey] = useState(false);
const changeColorButton = () => {
setIsGrey(!isGrey);
};
return (
<div
style={{ backgroundColor: isGrey ? 'grey' : 'default-color}}
id="friendDiv"
className="relative"
>
<div key={key} className="flex flex-row items-center space-x-6 mb-6">
<img src={project.image} />
<div>
<h1 key={key} class=" text-xl font-bold">
{project.name}
</h1>
</div>
<button
className="absolute right-10 bg-bgButtonAddPeople p-2"
onClick={changeColorButton}
>
Legg til
</button>
</div>
</div>
);
};
App.js
const App = () => {
return data.projects.map((project, key) => {
return <FriendDiv project={project} key={key} />;
});
};

Related

What ways are there to do an individual hover on elements that were iterated in React?

I'm rendering elements with the map function in react, the problem is that when hovering, the effect is applied to all the elements and what I'm looking for is an individual effect for each card.
This is my code:
const Card = () => {
const [isHovering, setIsHovering] = useState(false);
const handleMouseOver = () => {
setIsHovering(true);
};
const handleMouseOut = () => {
setIsHovering(false);
};
return (
<>
{productsList.map((product) => (
<div key={product.id}>
<div className="relative mb-4" onMouseOver={handleMouseOver} onMouseOut={handleMouseOut}>
<img src={product.img} alt="product" className="w-fit h-fit jshover cursor-pointer" />
{isHovering ? <CardOptions /> : ""}
</div>
<div className="flex justify-center flex-col px-3">
<h3 className="captialize font-sans font-bold text-black mb-3">Adicolor Classics Joggers</h3>
<div className="flex justify-between ">
<span className="capitalize font-normal font-sans text-[#777777]">Dress</span>
<span className="font-sans font-bold">$63.85</span>
</div>
</div>
</div>
))}
</>
)
}
I am iterating an external array of objects with the information of each card.
As seen in the image, I hover my mouse over a card and the "shop now" box appears on all cards.
What would be the best way to do it?
Without iteration of course it worked, but then using React is pointless.
Edit: [SOLVED] The state has to have the index of the iteration of the function. So the conditional rendering has to be conditioned on the index and not on a boolean value.
Like this:
const Card = () => {
const [isHovering, setIsHovering] = useState(-1);
const handleMouseOver = (item) => {
setIsHovering(item);
};
const handleMouseOut = () => {
setIsHovering(-1);
};
return (
<>
{productsList.map((product, index) => (
<div key={product.id}>
<div className="relative mb-4" onMouseOver={() => {
handleMouseOver(index);
}} onMouseOut={handleMouseOut}>
<img src={product.img} alt="product" className="w-fit h-fit jshover cursor-pointer" />
{isHovering === index ? <CardOptions /> : ""}
</div>
<div className="flex justify-center flex-col px-3">
<h3 className="captialize font-sans font-bold text-black mb-3">{product.title}</h3>
<div className="flex justify-between ">
<span className="capitalize font-normal font-sans text-[#777777]">{product.category}</span>
<span className="font-sans font-bold">{product.price}</span>
</div>
</div>
</div>
))}
</>
)

Uncaught Error: Rendered more hooks than during the previous render

Hello I am developing a page to search for gifs and I get the error when I search for a larger amount of gifs than I asked for the first time, for example if I ask for 15 gisf and then another 15 different gifs I get no error but if I ask for 15 and then 30 the error appears.
The gif grid component:
`
function Gifs(props: any) {
return (
<div className="gifs-container grid grid-cols-1 md:grid-cols-4 gap-5 ">
{props.gifList.map((gif: gif, index: number) => {
const [active, setActive] = useState(false);
return (
//img container
<div
className="gif-box w-[200px] h-[150px] relative text-white"
key={index}
>
{/* Image */}
<img
src={gif.url}
alt={gif.title}
className="w-full h-full object-cover"
/>
{/* Image overlay */}
<div
className="img-overlay absolute w-full h-full opacity-0 top-0 flex flex-col
items-center bg-[rgba(0,0,0,0.6)] transition duration-100 hover:opacity-100
justify-around overflow-hidden"
>
{/* Overlay Tittle */}
<div className="overlay-tittle font-bold text-xl text-center w-[80%]">
{notGif(gif.title)}
</div>
{/* Overlay Buttons */}
<div className="overlay-buttons grid-cols-2 w-[40%] flex justify-between items-center">
<button
className="viewLink w-8 h-8 hover:opacity-60"
onClick={() => {
window.open(gif.url, "_blank");
}}
></button>
<div className={`copyMessage ${active ? "active" : ""}`}>
<button
className="copyLink w-9 h-9 hover:opacity-60"
onClick={() => {
navigator.clipboard.writeText(gif.url);
setActive(true);
setTimeout(() => {
setActive(false);
}, 500);
}}
></button>
</div>
</div>
</div>
</div>
);
})}
</div>
);
}
export default Gifs;
the app component:
function App() {
const [gifList, setGifList] = useState<gif[]>([]);
const [gifCuantity, setGifCuantity] = useState("Cantidad");
const [gifName, setGifName] = useState("");
const setGifCuantityF = (newGifCuantity: string) => {
setGifCuantity(newGifCuantity);
};
const setGifNameF = (newGifName: string) => {
setGifName(newGifName);
};
const setGifListFun = (newGifList: gif[]) => {
setGifList(newGifList);
};
useEffect(() => {
if (gifCuantity !== "Cantidad" && gifName !== "") {
getGfifs(gifName, gifCuantity, setGifListFun);
setGifCuantity("Cantidad");
}
}, [gifName]);
return (
<div className="App">
<div className="h-screen ">
<div className="mx-auto flex flex-col justify-center items-center gap-8">
<Title />
<Browser
gifCuantity={gifCuantity}
setGifCuantityF={setGifCuantityF}
setGifNameF={setGifNameF}
/>
{/* <Cgifs gifList={gifList} /> */}
<Gifs gifList={gifList} />
</div>
</div>
</div>
);
}
`
i think the main proble is on the gifList useState, this is the useState that will contain all the gifs i fetch, so if the useState had array had 15 boxes and then i fetch for 20 gifs the error will say that the 5 new boxes were null before an now they are a useState

React component with list

I have created a Card component which also involves a list inside, a bit of this code below:
Card.js
export const Card = ({ feature }) => {
*some code*
return (
*some code*
<ul role="list" className="mt-6 space-y-6">
<li className="flex">
<CheckIcon className="flex-shrink-0 w-6 h-6 text-yellow-500" aria-hidden="true" />
<span className="ml-3 text-gray-500">{feature}</span>
</li>
</ul>
)
}
Now I use Card.js in another component ManyCards.js
const pricing = {
tiers: [
{
features: [
'Unlimited products',
'Unlimited subscribers',
'Advanced analytics',
'1-hour, dedicated support response time',
'Marketing automations',
'Custom integrations',
],
}
]
}
export default function ContractInfo() {
return (
<div className={formStep === 0 ? 'block' : 'hidden'}>
<div className="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:px-8">
<div className="mt space-y-12 lg:space-y-0 lg:grid lg:grid-cols-3 lg:gap-x-8">
{pricing.tiers.map((tier) => (
<Card
feature={tier.features.map((feature) => ())}
/>
))}
</div>
</div>
</div>
}
How do I get all features listed? I assume with map, but how exactly? I am super new to react. These are just bits of code, to demonstrate the issue, I am only stuck with the features.
iterate array using map similar to pricing.tiers just pass tier.features
export const Card = ({ feature }) => {
return (
<>
return (
<ul role="list" className="mt-6 space-y-6">
<li className="flex">
<CheckIcon
className="flex-shrink-0 w-6 h-6 text-yellow-500"
aria-hidden="true"
/>
<span className="ml-3 text-gray-500">{feature}</span>
</li>
</ul>
);
</>
);
};
export function ContractInfo() {
return (
<div className={formStep === 0 ? "block" : "hidden"}>
<div className="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:px-8">
<div className="mt space-y-12 lg:space-y-0 lg:grid lg:grid-cols-3 lg:gap-x-8">
{pricing.tiers.map((tier) => (
<Card
feature={pricing.tiers.map((tier) => (
<>
{tier.features.map((feature) => (
<Card feature={feature} />
))}
</>
))}
/>
))}
</div>
</div>
</div>
);
}

Can't store the return value of .map in NEXT.JS

I want to store the return value of the map into a variable but I'm getting this error of expected ) or const is reserve value. I can do this on react.js but I'm having this issue in next.js
<div className='w-3/5 flex flex-wrap justify-between mx-auto pt-24'>
{props.map((prop) => (
let rating = `${prop.review_scores.review_scores_accuracy}`;
<div
key={prop._id}
class='max-w-sm overflow-hidden shadow-xl flex-shrink'
>
<img
class='w-full'
src={prop.images.picture_url}
alt='Mountain'
/>
<div class='px-6 py-4'>
<h2 class='text-md font-bold mb-1'>{prop.name}</h2>
<p class='text-gray-700 text-base truncate'>
{prop.description}
</p>
<div>
{[
...Array(fullName),
].map((star, index) => {
return (
<FontAwesomeIcon
key={index}
icon={faStar}
className='w-4 h-4 fill-current text-indigo-900'
/>
);
})}
</div>
</div>
</div>
))}
</div>
Replace this part of code:
{props.map((prop) => (
let rating = `${prop.review_scores.review_scores_accuracy}`;
<div
key={prop._id}
class='max-w-sm overflow-hidden shadow-xl flex-shrink'
>
with:
{props.map((prop) => {
let rating = `${prop.review_scores.review_scores_accuracy}`;
return (
<div
key={prop._id}
class='max-w-sm overflow-hidden shadow-xl flex-shrink'
>
[REST_OF_CODE]
)}

How to open one dropdown item?

friends, I have array of questions, and a dropdown list for them... i want to open any question, but all questions are opening together... please help
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState(false)
const toggle = (id) => {
questions.forEach((q) => {
if(q.id === id){
setIsOpenAnswer((prevState) => !prevState)
}
})
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
Use a Javascript object to track which unique q.id is being set to true.
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState({})
const toggle = (id) => {
setIsOpenAnswer(prevState => ({
...prevState,
[id]: !prevState[id],
});
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer[q.id] && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
You're using the same prop for all of them here:
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
Try saving something unique in state to identify what you're supposed to be showing, e.g.,
{selectedQuestionId && /* the rest */ }
and set the selectedQuestionId where you're currently setting isOpenAnswer .

Resources