Displaying text on only one image at at time in React - reactjs

I am working on a portfolio page using React and Tailwind. I am trying to get to where when you hover over each image, it will display the text at the bottom of a single image. Right now when I hover over the image, all text displays on all images. How would I fix this? Also, I know there has to be a way to clean this up, I was trying to get it work first. Thank you for and advice you might have!
const Experience = () => {
const [isHovering, setIsHovering] = useState(false);
const handleMouseOver = () => {
setIsHovering(true);
}
const handleMouseOut = () => {
setIsHovering(false);
}
return (
<div
name="experience"
className="w-full h-screen bg-[#0a192f] text-gray-300"
>
<div className="max-w-[1000px] mx-auto flex flex-col justify-center w-full h-full">
<div>
<p className="text-4xl font-bold inline border-b-4 border-purple-800">
Experience
</p>
<p className="py-4">Technologies I have worked with</p>
</div>
<div className="w-full grid grid-cols-2 sm:grid-cols-4 gap-4 text-center py-8">
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={CssLogo}
alt="Css Logo"
/>
{isHovering ? <p className="font-bold">CSS</p> : ""}
</div>
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={HtmlLogo}
alt="HTML logo"
/>
{isHovering ? <p className="font-bold">HTML</p> : handleMouseOut}
</div>
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={JavaScriptLogo}
alt="Js Logo"
/>
{isHovering ? (
<p className="font-bold">JavaScript</p>
) : (
handleMouseOut
)}
</div>
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={MongoLogo}
alt="Mongo Logo"
/>
{isHovering ? (
<p className="font-bold">Mongo DB</p>
) : (
''
)}
</div>
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={MysqlLogo}
alt="Mysql Logo"
/>
{isHovering ? (
<p className="font-bold">MySQL</p>
) : (
''
)}
</div>
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={NodeLogo}
alt="Node Logo"
/>
{isHovering ? (
<p className="font-bold">Node JS</p>
) : (
''
)}
</div>

You have lots of text elements, but only one state value:
const [isHovering, setIsHovering] = useState(false);
So either all of them are "hovering" or all of them are "not hovering". Instead of a boolean, you might use some kind of identifier. For example:
const [hoveringID, setHoveringID] = useState();
And update the state to an identifier:
const handleMouseOver = (id) => {
setHoveringID(id);
}
const handleMouseOut = () => {
setHoveringID(undefined);
}
And pass some ID to those handlers:
onMouseOver={() => handleMouseOver(1)}
(Use a different ID for each element, of course.) And use that state to determine if that element is "hovering":
{hoveringID === 1 ? <p className="font-bold">CSS</p> : ""}
Taking it a step further, this all becomes much easier if you refactor all of that repeated code. Create data for your component:
const sections = [
{
id: 1,
src: CssLogo,
alt: "Css Logo",
hoverContent: "CSS"
},
//etc...
]
And just map over that data:
<div className="w-full grid grid-cols-2 sm:grid-cols-4 gap-4 text-center py-8">
{
sections.map(section => (
<div className="shadow-md shadow-[#040c16] hover:scale-110 duration-500">
<img
onMouseOver={() => handleMouseOver(section.id)}
onMouseOut={handleMouseOut}
className="w-20 mx-auto"
src={section.src}
alt={section.alt}
/>
{hoveringID === section.id ? <p className="font-bold">{section.hoverContent}</p> : ""}
</div>
))
}
</div>

Related

Why does my Layout of the carousel render vertically while flexed?

I have an carousel set up for view of store products.
Here you can test the code out here in the demo snippet Codesandbox
Mapping through my array of images it renders vertically on the screen when they should over lap each other given the view of a carousel to click from left to right. The container is relative to its position, flexed with 0 inset. I don't understand how the images render vertically
export default function Storetwo(){
const [currentSlide, setCurrentSlide] = useState(0);
const [currentProduct, setCurrentProduct] = useState(slides[0]);
const { handleSubmit, reset, setValue, control } = useForm({ defaultValues });
const [data, setData] = useState(null);
const onSubmit = data => console.log(data);
const handlePreviousClick = () => {
setCurrentSlide(currentSlide === 0 ? slides.length - 1 : currentSlide - 1);
setCurrentProduct(slides[currentSlide]);
}
const handleNextClick = () => {
setCurrentSlide(currentSlide === slides.length - 1 ? 0 : currentSlide + 1);
setCurrentProduct(slides[currentSlide]);
}
return(
<div className=' md:flex p-2 h-[100vh] '>
{/* image container */}
<div className=" w-[100%] md:w-[100%] ">
{slides.map((slide, index) => (
<div key={index} className={`relative flex inset-0 z-10 justify-center items-center ${index === currentSlide ? 'block' : 'hidden'}`}>
<Image src={slide.image} width={200} height={200} key={index} alt="" className="object-cover p-10 " />
<div className=" top-40 left-10 justify-center items-center mx-auto flex ">
<button className='text-3xl ' onClick={handlePreviousClick}><ArrowBackIosIcon/></button>
<button className='text-3xl ' onClick={handleNextClick}><ArrowForwardIosIcon /></button>
</div>
</div>
))}
</div>
.../
Here are the vertical rendering images
Your link is broken, which is one of the reasons why external live examples aren't recommended on StackOverflow. Use Snippets instead.
As for your question, I believe you're misunderstanding how flexbox works (or maybe it's just an oversight).
You're applying flex to the divs that are being rendered as a result of your mapping, when you should be adding it to their container instead. It's the container that will set its children position as flexbox. Read more here.
[...]
return(
<div className=' md:flex p-2 h-[100vh] '>
{/* image container */}
<div className="flex w-[100%] md:w-[100%] "> {/* <= here */}
{slides.map((slide, index) => (
<div key={index} className={`relative flex inset-0 z-10 justify-center items-center ${index === currentSlide ? 'block' : 'hidden'}`}>
<Image src={slide.image} width={200} height={200} key={index} alt="" className="object-cover p-10 " />
<div className=" top-40 left-10 justify-center items-center mx-auto flex ">
<button className='text-3xl ' onClick={handlePreviousClick}><ArrowBackIosIcon/></button>
<button className='text-3xl ' onClick={handleNextClick}><ArrowForwardIosIcon /></button>
</div>
</div>
))}
</div>

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

button only clicks once, at one div element

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

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

Resources