Component Style Update Not Waiting on setTimeOut Function - javascript

I have a div with three children, and a button that when pushed changes their relative position to slide over into frame. As a view slides over into frame, the other views should not only slide out, but also become hidden (to not take up vertical space).
Currently, I'm using the 'visibility' css property to show/hide the div after transition, but that doesn't collapse the height of the parent container as the hidden div still takes up vertical space.
Code Using 'Visibility' Property
useEffect(() => {
updating position css attribute via style dict...
switch(activePage) {
case 1:
setPageOneVisible(true)
setTimeout(setPageTwoVisible(false), 500)
break
case 2:
setPageTwoVisible(true)
setTimeout(setPageOneVisible(false), 500)
setTimeout(setPageThreeVisible(false), 500)
break
case 3:
setPageThreeVisible(true)
setTimeout(setPageTwoVisible(false), 500)
}
}, [activePage])
Using the 'display: none' or 'height: 0' properties works in the sense that the parent container collapses, but for some reason, even if applying these properties using the setTimeOut() function to only trigger after the transition is completed, the divs get hidden instantaneously and don't wait for the timer.
Code Using 'Display' Property
useEffect(() => {
setPageOnePosDict(prevState => ({
...prevState,
['display']: pageOneVisible ? 'block' : 'none'
}))
setPageTwoPosDict(prevState => ({
...prevState,
['display']: pageTwoVisible ? 'block' : 'none'
}))
setPageThreePosDict(prevState => ({
...prevState,
['display']: pageThreeVisible ? 'block' : 'none'
}))
}, [pageOneVisible, pageTwoVisible, pageThreeVisible])
Why does react appear to ignore the useTimeOut function when the 'display' style is being added?
The JSX code for the 3 divs is:
<div id='screen1' className='relative w-full bg-blue-100 h-min overlap transition-all duration-500' style={pageOnePosDict}>
<div className='w-full h-[400px] bg-transparent text-center'>
<div className='pt-[200px]'>1</div>
</div>
</div>
<div id='screen2' className='relative w-full bg-green-100 h-min overlap transition-all duration-500' style={pageTwoPosDict}>
<div className='w-full h-[200px] bg-transparent text-center'>
<div className='pt-[100px]'>2</div>
</div>
</div>
<div id='screen3' className='relative w-full bg-purple-100 h-min overlap transition-all duration-500' style={pageThreePosDict}>
<div className='w-full h-[100px] bg-transparent text-center'>
<div className='pt-[40px]'>3</div>
</div>
</div>

The first argument to setTimeout should be a function, but you’re invoking the function and passing its return value to setTimeout. Invoking it triggers the change(s) immediately.
// do this
setTimeout(() => setPageTwoVisible(false), 500)
// not this
setTimeout(setPageTwoVisible(false), 500)

Related

Flow direction for divs displayed on hovering

I have a following set of images each with its own div and I want to show a popup with information on hover. Refer the following component code-
function Skill({ directionLeft, skill }: Props) {
return (
//group hover filter class in motion img used for special effect
<div className='group relative flex cursor-pointer'>
<motion.div
initial={{
x: directionLeft ? -200 : 200,
opacity: 0
}}
transition={{ duration: 0 }}
whileInView={{ opacity: 1, x: 0 }}
className='w-16 h-16 rounded-full border border-gray-500 object-cover xl:w-20 xl:h-20 filter group-hover:grayscale transition duration-300 ease-in-out'
>
<Image
src={urlFor(skill.skillImage).url()}
height={108} width={192} alt='' className=' w-16 h-16 rounded-full border border-gray-500 object-cover xl:w-20 xl:h-20 filter group-hover:grayscale transition duration-300 ease-in-out' />
</motion.div>
<div className='absolute opacity-0 group-hover:opacity-90 transition duration-300 ease-in-out z-20 group-hover:bg-[#1e313f] h-54 xl:w-72 rounded-lg p-5'>
<div className='flex flex-col items-center justify-center h-full'>
{/*loop over if multiple skill supports*/}
{skill.relevantWorkDone.map((relevantWorkDone, index) => (
<p key={index} className='uppercase tracking-[5px] text-gray-200 text-sm overflow-y-auto opacity-100 z-20= justify-center'>
-{relevantWorkDone}
</p>
))}
</div>
</div>
</div>
)
}
Now I want to adjust the hovering of the popup messages according to the location of the parent div in super parent grid.
Eg- instead of 1
I want something like 2-
The image div being hovered on is the one in the one in the last column if the 2nd last row.
I know I can pass a prop with the grid location like I and create a function depending upon the remainder to make it flow either left or right and if I is in the first half or second half of total elements to make it flow from either top or bottom.
But I don't know what tailwind CSS classes to use to achieve this.
Any help would be appreciated.

TailwindCSS scale class works inconsistently while being used with React state

I'm using react and tailwindcss to create a web app for news. Headline of the page looks like the picture below (the image and text were randomly chosen):
When I hover my mouse on the div the image is supposed to zoom in while changing the state on hover and setting the scale while based on the isMouseIn boolean value. But the problem is that it sometimes works but other times it doesn't. I tried checking the state value and it works perfectly, the values are getting set without any problems but it doesn't scale when I enter the page for the first time. If I refresh the page or change the scale while manually in the code, then it works but otherwise it doesn't. Is this maybe a bug from react or am I doing something wrong?
Check the code down below for a better understanding of my implementation:
export default function Headline() {
/*
*
* green border --> the whole component
* orange border --> div that is darkening the whole component
*/
const [isMouseIn, setMousePos] = useState(false);
function mouseEnter() {
setMousePos(true);
}
function mouseLeave() {
setMousePos(false);
}
return (
<div
onMouseEnter={() => mouseEnter()}
onMouseLeave={() => mouseLeave()}
className="mb-[.1em] overflow-hidden 950:rounded-xl cursor-pointer w-[100%] h-[300px] 380:h-[400px] 1215:h-[500px] max-h-[500px] relative shadow-2xl"
>
<img
src={headline}
className={`w-full scale-${
isMouseIn ? "150" : "100"
} object-cover transition-all duration-700 h-full max-h-full absolute`}
/>
.
.
.
.
.
We can use group modifier of tailwindcss.
Give "group" class the parent element
Give class names to child element according to the parent elements state
export default function Headline() {
return <div
className="
group mb-[.1em] overflow-hidden 950:rounded-xl cursor-pointer w-[100%]
h-[300px] 380:h-[400px] 1215:h-[500px] max-h-[500px] relative shadow-2xl"
>
<img
src={"https://i.stack.imgur.com/6TEEa.png"}
className="
object-cover transition-all duration-700 h-full max-h-full absolute
group-hover:scale-150 scale-100"
/>
</div>;
};
Referance
Tailwind CSS Doc: Group Modifier

How to get parent div in handleChange with React

I have multiple divs that are dynamic (it depends on which filter the visitor checked). So I can not use getElementById.
I need to get the parent div to change its CSS if an input is checked.
Here is my code:
{workout?.map(x => {
return <div className='relative bg-gray-200 p-4 rounded-md my-3 mx-2' key={x.name}>
<input onClick={handleChecked} className='absolute right-0 top-0' type="checkbox" />
<div className='flex'>
<img className='w-2/6 rounded mr-5' src={`${x.path}`} alt={x.name} />
<div className='w-4/6'>
<h2 className='text-xl'>{x.name}</h2>
<p>Nombre de séries : {x.set}</p>
<p>Nombre de rép : {x.reps}</p>
{x.secondary ? <p>Muscles solicités : <br />
<span className='space-x-2'>
{x?.secondary?.map(k => {
return <span className='bg-white px-1 rounded-md' key={k}>{k}</span>
})}
</span>
</p> : null}
</div>
</div>
</div>
})}
The idea, is to add a border-2 border-teal-500 class to the parent div of the input when it is checked.
Here is my handleChecked:
function handleChecked(e) {
// code here
}
I saw that I had to use parentNode but the problem is, I can't store in a variable the current input because it is dynamic. Every item has its own input.
Any idea how I can handle this?
You shouldn't be using getElementById (or any other vanilla JS DOM method) in React anyway - store and change values in state instead. You can leverage this by making input a controlled component, and storing the checked values in an array, or in workout - then, when returning the JSX, you just need to check whether the x variable (the item being iterated over) indicates that the current input should be checked or not - which will also tell you whether the parent should have a different class.
const makeHandleChecked = (i) => (e) = {
setWorkout(
workout.map(
(w, j) => j !== i ? w : { ...w, checked: e.target.checked }
)
);
};
{workout?.map((x, i) => (
<div className={`relative bg-gray-200 p-4 rounded-md my-3 mx-2${x.checked ? ' checked' : ''}`} key={x.name}>
<input onClick={makeHandleChecked(i)} checked={x.checked} className='absolute right-0 top-0' type="checkbox" />

TailwindCSS animations on conditionally rendered components in React

If I am conditionally rendering a component depending on some state, how can I animate its transition between its open and closed states in React with TailwindCSS?
{successMessage && (
<div className="flex transition-all ease-in-out duration-300 bg-gray-200 w-44 items-center justify-between px-2 rounded">
<p>Added to watchlist!</p>
<button onClick={() => setSuccessMessage(false)}>X</button>
</div>
)}
This code half works but there is no animation or transition period to it. How can I fix this?
Try something like this :
<div className={`flex transition-all ease-in-out duration-300 bg-gray-200 w-44 items-center justify-between px-2 rounded ${your_state ? 'opacity-100' : 'opacity-0'}`}>
...
</div>
The "official" solution is to use Headless UI's Transition component.
Headless UI is created and maintained by the same authors of Tailwind CSS.
Two states works great.
With first one add/remove "hidden" class.
And with second one change opacity/height/translate or what you need for animation.
Use useEffect and setTimeout with 0 delay for changing the state of secondState. like below:
useEffect(() => {
setTimeout(() => {
setSecondState(firstState)
}, 0) }, [firstState])
<div className={`${firstState ? "hidden" : ""} ${secondState ? "opacity-100" : "opacity-0"}`} />
I would recommend AOS libary to do this, as you would need to do some more work to make this work with TailwindCSS.
Try this
Install Aos
npm install aos --save
Put in your file
import React, { useEffect } from "react";
import AOS from 'aos';
import "aos/dist/aos.css";
useEffect(() => {
AOS.init({
duration : 1000
});
}, []);
You can change duration as you like.
Add as attribute to HTML tag
data-aos="animation_name"
Example
<div> data-aos="fadeIn" </div>
Extra Attributes you can use
data-aos="fade-up"
data-aos-offset="200"
data-aos-delay="50"
data-aos-duration="1000"
data-aos-easing="ease-in-out"
data-aos-mirror="true"
data-aos-once="false"
data-aos-anchor-placement="top-center"
List of all animation
Documentation

Prevent moving text elements on hover which is +1px font size

I have a row with text elements, in this case they are named "categories", any category that is on hover gets added a class that has font-size 1px bigger than non hover. Font size of my category may be diffrent (all categories have the same font but in config it can be changed) and comes from outside config.
const currentCategoryStyle = {
fontSize: `${parseInt(singleCategory['font-size'].replace('px', '')) + 1}px`,
};
<div className="categories-container d-flex">
{categories.map(category => (
<div
className="single-category"
onMouseEnter={() => {
setCategoryHover(category.id);
}}
onMouseLeave={() => {
setCategoryHover(null);
}}
style={{
...(
categoryHover === category.id &&
currentCategoryStyle),
}}
>
{category.name}
</div>
))}
</div>
But the problem is, that every element moves a bit, when I hover one category, of course because it gets bigger. My question is, what can I do to prevent this? Margin, padding doesn't really work here, any ideas?

Categories

Resources