How can I add animation to react state - javascript

I just want to add a fade in animation to next index. i found a package called "react transition group" but all tutorials were based on class components or redux I didn't understand anything.
const AboutTestimonials = () => {
const [index, setIndex] = useState<any>(0);
const [data] = useState(AddTestimonial);
const current = data[index];
return (
<div className="testimonials__container">
<div className="testimonials__description">
<h3>TESTIMONIALS</h3>
<p>{current.quote}</p>
<h5 className="author__testimonials">{current.postedby}</h5>
<h6 className="job__testimonials">{current.profession}</h6>
</div>
<div className="testimonials__pagination">
<Image
src={leftarrow}
alt="arrow"
onClick={() => setIndex(index > 0 ? index - 1 : index)}
className="pagination__arrows"
/>
<p>{index + 1} / 5</p>
<Image
src={rightarrow}
alt="arrow"
onClick={() => setIndex(index < 4 ? index + 1 : index)}
className="pagination__arrows"
/>
</div>

SwitchTransition waits for the old child to exit then render the new child as mentioned in the react transition group website.
there are two modes:
in-out
out-in
the important factor is changing the key prop of the child component.child component could be CSSTransition or Transition.if you want the transition changes simultaneously you can use the TransitionGroup.
side note: if you want to use the AddTestimonial in your component and you don't want to change the state (I noticed there is no second argument for setting the data), there is no need to declare a useState.it is much better to set AddTestimonial as a prop on AboutTestimonials component
import { CSSTransition, SwitchTransition } from 'react-transition-group';
const AboutTestimonials = () => {
const [index, setIndex] = useState<any>(0);
const [data] = useState(AddTestimonial);
const current = data[index];
return (
<div className="testimonials__container">
<div className="testimonials__description">
<h3>TESTIMONIALS</h3>
<SwitchTransition mode={'out-in'} >
<CSSTransition
key={index}
timeout={300}
classNames="fade"
>
<>
<p>{current.quote}</p>
<h5 className="author__testimonials">{current.postedby}</h5>
<h6 className="job__testimonials">{current.profession}</h6>
</>
</CSSTransition>
</SwitchTransition>
</div>
<div className="testimonials__pagination">
<Image
src={leftarrow}
alt="arrow"
onClick={() => setIndex(index > 0 ? index - 1 : index)}
className="pagination__arrows"
/>
<p>{index + 1} / 5</p>
<Image
src={rightarrow}
alt="arrow"
onClick={() => setIndex(index < 4 ? index + 1 : index)}
className="pagination__arrows"
/>
</div>
)}
css:
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 300ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 300ms;
}

Related

Custom slider component in React is not working as expected

I am trying to improve my skills in React here and I wanted to build my portfolio, so
I decided to have custom slider instead of using a library, so I use the following code:
const ProjectWrapper = styled.div`
.container {
transform: translateX(-${(props) => props.activeIndex * 200}px);
transition: transform 0.3s;
display: flex;
flex-direction: column;
margin: 20px;
border: 1px solid gray;
border-radius: 5px;
padding: 20px;
text-align: justify;
color: white;
text-decoration: none;
height: 450px;
}
`;
export default function Portfolio() {
const [activeIndex, setActiveIndex] = useState(0);
const updateIndex = (newIndex) => {
if (newIndex < 0) {
newIndex = projects.count - 1;
} else if (newIndex >= projects.count) {
newIndex = 0;
}
setActiveIndex(newIndex);
};
return (
<div>
<Arrow>
<ProjectWrapper activeIndex={activeIndex}>
{projects.map((el, idx) => {
return (
<a key={idx} className="container" href={el.url}>
<div>
<div className="img">
<img src={el.image} alt={el.title} />
</div>
<div className="row">
<h3 className="title">
{el.title}
<a target="_blank" href={el.github}>
{el.github === "" ? (
""
) : (
<i className="fa-brands fa-github fa-lg"></i>
)}
</a>
</h3>
<div className="desc">
<p>{el.description}</p>
</div>
<p>
<b>Technologies:</b> {el.resources}
</p>
</div>
</div>
</a>
);
})}
</ProjectWrapper>
<button
onClick={() => {
updateIndex(activeIndex - 1);
}}
>
{" "}
<i class="fa-solid fa-angle-left"></i>
</button>
<button
onClick={() => {
updateIndex(activeIndex + 1);
}}
>
<i class="fa-solid fa-angle-right"></i>
</button>
</Arrow>
</div>
);
}
It is working pretty fine except for two issues:
After showing the last card I want arrow for next to not be clickable;
After going next, and then clicking back to the very beginning, the arror for next is not clickable anymore. After refreshing the page, I am able again to go to the next cards.
Anyone any idea what can I improve on my code here?
To disable the "Next" button, add a conditional disabled flag to it. This example will disable the button when the state is equal to the total number of projects:
<button
onClick={() => {
updateIndex(activeIndex + 1);
}}
disabled={activeIndex === (projects.count - 1)}
/>
For your second problem, your function is currently setting the index to 0 once you reach the last slide. So you are on slide 5/5, but your function is setting the index to 0:
else if (newIndex >= projects.count) {
newIndex = 0;
}
This is muddling the logic, so I would recommend removing updateIndex() entirely and writing your buttons like so:
<button
onClick={() => setActiveIndex((prevIndex) => prevIndex + 1)}
>
<i class="fa-solid fa-angle-right"></i>
</button>
Or - 1 for the back button.

How do I use AnimatePresence with AnimatedSharedLayout in framer motion?

I have a container which can possible display several child containers based on state. When the state changes (based on a button click), I would like:
The current container to fade out
The height of the main container to animate to the be the height of its new child container
The new child container to fade in
I feel like I'm on the right track with my demo, but the AnimatePresence seems to be ignored as none of the opacity animations are working. I'm hoping someone can please take a look and let me know what I need to adjust?
https://codesandbox.io/s/animating-shared-layouts-with-content-changing-h8hvq?file=/src/App.js
function Home() {
return (
<div>
<div className="row" />
<div className="row" />
</div>
);
}
function Contact() {
return (
<div>
<div className="row" />
<div className="row" />
<div className="row" />
<div className="row" />
<div className="row" />
</div>
);
}
const sections = {
home: "Home",
contact: "Contact"
};
export default function App() {
const [activeSection, setActiveSection] = useState(sections.home);
return (
<div className="outer-container">
<AnimateSharedLayout>
<motion.div layout initial={{ borderRadius: 25 }}>
<h3>Text on top</h3>
<AnimatePresence key={activeSection} exitBeforeEnter initial={false}>
<motion.div
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { delay: 1.2, duration: 1 }
}}
exit={{
opacity: 0,
transition: { delay: 0, duration: 1 }
}}
>
<div className="inner-container">
<div className="content">
<div className="avatar" />
{activeSection === sections.home && <Home />}
{activeSection === sections.contact && <Contact />}
</div>
</div>
</motion.div>
</AnimatePresence>
</motion.div>
</AnimateSharedLayout>
<button
type="button"
onClick={() =>
setActiveSection(
activeSection === sections.home ? sections.contact : sections.home
)
}
>
Go to {activeSection === sections.home ? "Contact" : "Home"} section
</button>
</div>
);
}

Why is useState returning undefined?

I'm attempting to use useState to alter the display type in my styled components. When attempting to use my code the display type is not altered and my variable "displayType" is undefined.
I've attempted altering what my setStyle() function returns, but I am starting to see this is a larger problem that I'd like to understand better.
When I print the value to the console in index.js everything works fine. However I just get undefined when I try to use displayType in StoreElements.js
src/pages/store.js
const [displayType, setDisplayType] = useState("none");
const setStyle = (displayType) => {
setDisplayType(displayType);
console.log(displayType)
};
const [isOpen, setIsOpen] = useState(false)
const toggle = () => {
setIsOpen(!isOpen)
}
return (
<div>
<Sidebar isOpen={isOpen} toggle={toggle} />
<Navbar toggle={toggle} />
<Store setStyle={setStyle} displayType={displayType}></Store>
<Footer />
</div>
)
}
export default StorePage
src/store/index.js
const Store = ({displayType, setStyle}) => {
return (
<>
<AboutBg style={{ backgroundImage: `url(${BgPic})` }}></AboutBg>
<StoreContainer>
<StoreWrapper>
<Title>Store</Title>
<ItemsContainer>
<ItemWrapper
onMouseEnter={() => setStyle("hoodie")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/about">
<MerchImg src={frontHoodie}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>Hoodie text</MerchText>
<HoodiePriceText>price</HoodiePriceText>
</TextWrapper>
</ItemWrapper>
<ItemWrapper
onMouseEnter={() => setStyle("sweats")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/tournaments">
<MerchImg src={frontSweats}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>Sweats text</MerchText>
<SweatsPriceText displayType={displayType}>
price
</SweatsPriceText>
</TextWrapper>
</ItemWrapper>
<ItemWrapper
onMouseEnter={() => setStyle("shirt")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/">
<MerchImg src={frontShirt}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>Shirt text</MerchText>
<ShirtPriceText>price</ShirtPriceText>
</TextWrapper>
</ItemWrapper>
<ItemWrapper
onMouseEnter={() => setStyle("mousepad")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/">
<MerchImg src={mousepadFront}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>mouspad text</MerchText>
<MousepadPriceText>price</MousepadPriceText>
</TextWrapper>
</ItemWrapper>
</ItemsContainer>
</StoreWrapper>
</StoreContainer>
<div>
{listItems}
{cartItems}
Total: ${cartTotal}
{cartItems.length}
</div>
</>
);
};
export default Store;
src/store/StoreElements.js
export const HoodiePriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
display: ${({ displayType }) => {
if (displayType === "hoodie") {
console.log("working");
return "block";
} else {
console.log({displayType})
return "none";
}
}};
`;
export const ShirtPriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
`;
export const MousepadPriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
`;
export const SweatsPriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
`;
In your styled component usage, you should bind the property displayType:
<HoodiePriceText displayType={displayType}>price</HoodiePriceText>
Thus, you should able get displayType in styled component!
setDisplayType is triggering the state change and causes a re-render of the function. It does not modify the value of the variable displayType. The value displayType is still undefined directly after calling setDisplayType, because it only gets its value after the function re-runs the useState-line.
const [displayType, setDisplayType] = useState("none");
// displayType can only get a new value here

excluding missing data when mapping through JSON in React/Js

I have a React component set up to map through a JSON file of projects and display the information in a card. However some of projects have less information that others. Namely my wordpress website does not need a link to the code. However I have it set up like:
Code:
<p>{project.code}</p>
How can I change this to an if statement, saying if Code is a property then return that block of code or else do not display 'code:' at all.
Here is my two files for reference.
Projects.js:
import React from "react";
import ReactCardFlip from "react-card-flip";
import Data from "../../ProjectData.json";
import './Projects.scss'
import '../MediaQueries/Projects.scss'
const CardStyle = {
padding: "30px",
margin: "30px",
width: "250px",
height: "300px",
};
const Card = ({ project }) => {
const [isFlipped, setIsFlipped] = React.useState(false);
// console.log(project);
return (
<div className="Card-wrapper">
<ReactCardFlip isFlipped={isFlipped} flipDirection="horizontal">
<div
style={CardStyle}
onMouseEnter={() => setIsFlipped((prev) => !prev)}
className="CardFront"
>
<div className="CardFront-div1">
<h3 className="CardFront-div1-title">{project.title}</h3>
<img width="250" src={project.gif} alt="" className="CardFront-div1-gif"/>
<div className="CardFront-div1-list">
<p>{project.html}</p>
<p>{project.css}</p>
<p>{project.javascript}</p>
<p>{project.react}</p>
</div>
</div>
</div>
<div
style={CardStyle}
onMouseLeave={() => setIsFlipped((prev) => !prev)}
className="CardBack"
>
<div>
<p>{project.description}</p>
<span>
Project:
<p>{project.link}</p>
</span>
<span>
Code:
<p>{project.code}</p>
</span>
</div>
</div>
</ReactCardFlip>
<button onClick={() => setIsFlipped((prev) => !prev)} className="cardflip-button">Flip</button>
</div>
);
};
const Projects = () => {
return (
<>
<h1>Projects</h1>
<div className="Projects" id="Projects">
{Data.map((item, index) => (
<Card project={item} key={`card-${index}`} />
))}
</div>
</>
);
};
export default Projects;
{
project.code ? (
<span>
Code:
<p>{project.code}</p>
</span>
) : null
}
Post got deleted by the user but the code is what I was looking for:
{!!project.code && (
<span >
Code:
<p>{project.code}</p>
</span>
)}

How to change icons with React Spring?

I want to change the icon when I click on it with react spring. For example, when I click on "🤪", it will change into "😄". In the documentation of react spring, it's possible to make it with the transition props, but how do I toggle it with onClick?
https://www.react-spring.io/docs/props/transition
the following codes are provided by react spring
<Transition
items={toggle}
from={{ position: 'absolute', opacity: 0 }}
enter={{ opacity: 1 }}
leave={{ opacity: 0 }}>
{toggle =>
toggle
? props => <div style={props}>😄</div>
: props => <div style={props}>🤪</div>
}
</Transition>
create a button and change toggle value on click:
function App() {
const [toggle, setToggle] = React.useState(false);
return (
<>
<button onClick={() => setToggle(!toggle)}>toggle</button>
<Transition
items={toggle}
from={{ position: "absolute", opacity: 0 }}
enter={{ opacity: 1 }}
leave={{ opacity: 0 }}
>
{toggle =>
toggle
? props => <div style={props}>😄</div>
: props => <div style={props}>🤪</div>
}
</Transition>
</>
);
}

Categories

Resources