Remove class after adding / Restart CSS animation onClick in React - javascript

I'm using CSS transform to animate an AwesomeFont icon when I click it
.animate {
-webkit-animation-duration: 400ms;
-webkit-animation-name: animation
}
AFAIK, to trigger the animation I need to add the above class to an element after it's being rendered. So I do this in React
class Class extends Component {
state = {
likes: 5,
play: false
}
handleClick = () => {
this.setState((p) => { return {play: p.play ? false : true}; })
}
render() {
<a onClick={this.handleClick} href="#" />
<Icon className={this.state.play ? 'animate' : ''} name="thumbs-up" />
{this.state.likes}
</a>
}
}
which works pretty well if I'm just toggling the states with each click, but now I want to trigger the animation on every click
...
handleClick = () => {
this.setState((p) => { return {play: p.play ? false : true}; })
}
render() {
<a onClick={this.handleClick} onMouseUp={() => this.setState(()=>{return {play:false}; })} href="#" />
<Icon className={this.state.play ? 'animate' : ''} name="thumbs-up" />
{this.state.likes}
</a>
}
}
It's not pretty, but I just want to illustrate a point, and I don't think it is working as it should because clicking on the number part of the link repeatedly doesn't trigger the animation but clicking the Icon does.
I tried adding the class onMouseDown and removing it onMouseUp but React just batches up the setStates, resulting in nothing. Anyone with any ideas how should I approach this problem?

Please try to change a tag to div tag. The click and other mouse events work fine on div and it's child elements. See an example:
<div id='wrapper' onClick='wrapperClick()' onMouseUp='wrapperMouseUp()'>
This is parent
<div id='child'>
I am child
</div>
</div>
#wrapper {
width:100px;
height:100px;
border:1px solid black;
}
#child {
padding-top:10px;
border:1px solid red;
}
function wrapperClick() {
alert('you clicked me')
}
function wrapperMouseUp() {
alert('Mouse Up')
}
Just note that mouse up fires before click event.
handleMouseUp = () => {
setCounter()
}
handleClick = () => {
setCounter()
}
setCounter() {
this.setState((p) => { return {play: p.play ? false : true}; })
}
render() {
<div onClick={this.handleClick} onMouseUp={this.handleMouseUp} href="#" />
<Icon className={this.state.play ? 'animate' : ''} name="thumbs-up" />
{this.state.likes}
</div>
}
}

Related

Click OutSide co close sidebar reactjs

I create a function when I click outside of the sidebar it will hide it and I also have a button that toggles show and hide the sidebar. But when I combined both of them together, the button did not work properly, it only show the sidebar but can't close it, only when I click outside it will close the sidebar
Click OutSide to close function:
const ref = useRef(null);
useEffect(() => {
document.addEventListener("mousedown", Clickout);
return () => {
document.removeEventListener("mousedown", Clickout);
};
}, []);
const Clickout = (eve) => {
if (ref.current && !ref.current.contains(eve.target)) {
setShow(false);
}
};
My Return:
return (
<header>
<div className="head">
<div className="logo">
<img src={logo} alt="logo" />
</div>
<button
className="burger"
onClick={() => {
setShow(!showMenu);
console.log("here");
}}
>
<div className={`${showMenu ? "change" : ""} bur1 `}></div>
<div className={`${showMenu ? "change" : ""} bur2 `}></div>
<div className={`${showMenu ? "change" : ""} bur3 `}></div>
</button>
</div>
<nav className={showMenu ? "active" : ""} ref={ref}>
<ul>
{navItem.map((item) => {
const { id, url, text } = item;
return (
<li key={id}>
<a href={url}>{text}</a>
</li>
);
})}
</ul>
</nav>
</header>
);
};
Nav bar CSS:
nav {
position: fixed;
right: -100%;
top: 0;
width: 60%;
height: 100vh;
text-align: center;
padding-top: 15vh;
transition: 0.8s ease;
background-color: blue;
}
nav.active {
right: 0;
transition: 0.5s;
}
Thank you.
you can use another state for manage button onclick when menu is open:
const [disableBtn, setDisableBtn] = useState(false);
and in Clickout function manage it:
const Clickout = (eve) => {
if (showMenu && ref.current && !ref.current.contains(eve.target)) {
setShow(false);
setDisableBtn(true)
} else {
setDisableBtn(false)
}
};
and in button for onclick use condition:
if (!disableBtn) setShow(true);
Updating state this way setShow(!showMenu) does not immediately update the state.Rather it schedules the update(You can read the docs). When your setState depends on your previous state (in this case showMenu depends on previous state) use this technique: (prev) => setState(!prev) instead. So, simply updating your onClick will solve the issue.
<button className="burger"
onClick={() => {
(prevShowMenu) => setShow(!prevShowMenu)
}}>
(Let me know in the comments if this was helpful)

How to efficiently toggle classes with multiple HTML elements across multiple files

I am toggling an arrow image whenever my div is clicked. I am controlling the state of the click with
const [toggleArrow, setToggleArrow] = useState(false)
My div has an onClick function that controls the state and toggles the faq-subject-toggle-arrow class that adds an image
<div className={`faq-subject ${toggleArrow ? 'faq-subject-toggle-arrow' : ''}`} onClick={() => {
setToggleArrow(!toggleArrow)
}>
My problem is that I have 50 divs across multiple styles and don't want to make 50 states to toggle one image.
Is there a more efficient solution for this with less code?
I created something that does the same thing. I extracted your code and made a component. now the state lives inside the component and will not affect others.
live demo
component
import { useState } from "react";
export const Button = () => {
const [toggleArrow, setToggleArrow] = useState(false);
return (
<div
style={{ width: "50px", height: "50px", margin: "10px" }}
className={`faq-subject ${toggleArrow ? "faq-subject-toggle-arrow" : ""}`}
onClick={() => {
setToggleArrow(!toggleArrow);
}}
></div>
);
};
css file, I didn't have the same classes so I created mine.
.faq-subject {
background: blue;
}
.faq-subject-toggle-arrow {
background: orange;
}
now you can use it wherever you want for multiple times
import { Button } from "./button";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Button />
<Button />
<Button />
<Button />
<Button />
</div>
);
}

How to animate a sliding cart with react spring with a toggle button

I have almost got this workign but not quite sure what I am doing wrong. It will slide in when I click the toggle button, but it wont slide out when I click it again, it will just rerun the slide in animation.
Any help would be great
I have the following state and toggle function
const [close, setClose] = useState(false)
const toggleCart = () => {
setClose(!close)
}
following component
<CartItems close={close} location={location} />
import React, { useState } from "react"
import tw, { styled } from "twin.macro"
import { useTransition, animated } from "react-spring"
const CartWrapper = styled.div`
.test {
position: fixed;
top: 0px;
z-index: 5000;
right: 0;
height: 100vh;
background: lightgrey;
padding: 25px;
}
`
export function CartItems({ location, close }) {
const transitions = useTransition(close, null, {
enter: { transform: "translate3d(100%,0,0)" },
leave: { transform: "translate3d(0%,0,0)" },
})
return (
<>
<CartWrapper>
{transitions.map(({ props }) => {
return (
<animated.div className="test" style={props}>
<h2>Shopping Cart</h2>
{cart}
<p>Total: {formattedTotalPrice}</p>
<form onSubmit={handleSubmitCheckout}>
{/* include validation with required or other standard HTML validation rules */}
<input
name="name"
placeholder="Name:"
type="text"
onChange={e => setName(e.target.value)}
/>
<input
name="giftMessage"
placeholder="Gift Message:"
type="text"
onChange={e => setGiftMessage(e.target.value)}
/>
<input type="submit" />
</form>
<button onClick={clearCart}>Remove all items</button>
</animated.div>
)
})}
{/* <button onClick={handleSubmit}>Checkout</button> */}
</CartWrapper>
</>
)
}
In your example there is a second item during the transition, one entering, and one leaving. That's why you see always the entering animation.
If you use a boolean instead of array in the useTransition you have to insert a condition in the render method to prevent the second item. Just like the third example in the useTransition doc. https://www.react-spring.io/docs/hooks/use-transition
transitions.map(({ item, props, key }) => {
return (
item && <animated.div className="test" style={props} key={key}>
Now it basically works, but a slight modification in the useTransition is necessary.
const transitions = useTransition(close, null, {
from: { transform: "translate3d(100%,0,0)" },
enter: { transform: "translate3d(0%,0,0)" },
leave: { transform: "translate3d(100%,0,0)" }
});
I have a working example here: https://codesandbox.io/s/toggle-react-spring-transition-ju2jd

How to close a modal with a handleClick, outside of the modal?

Actually, I use isAvatarUserMenuOpen prop into the UserDataPresentation class, to know if the modal is opened or not. I use this state to generate a condition that affect the onClick to open and close the modal. But i need to close this modal with any click made outside this modal, actually it only closes in the same button that open it.
I have been doing a handleClick, that add a listener when the modal is opened, and when i click outside the modal, it shows an alert "close de modal!". I need to remove this alert, and find a way to close the modal, just as the onclick that open and close the modal do.
export class UserDataPresentation extends React.Component<Props> {
node: any
componentWillMount() {
document.addEventListener('mousedown', this.handleClick, false)
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClick, false)
}
handleClick = (e: { target: any }) => {
if (!this.node.contains(e.target)) {
alert('close de modal!')
return
}
}
render() {
const { openMenuUserModal, closeMenuUserModal, isAvatarUserMenuOpen } = this.props
return (
<React.Fragment>
<div className="user-data-container" ref={isAvatarUserMenuOpen ? (node) => (this.node = node) : null}>
<div className="text">
<p>Wolfgang Amadeus</p>
</div>
<div className="avatar">
<img src={avatarPhoto} />
</div>
<a href="#" onClick={isAvatarUserMenuOpen ? closeMenuUserModal : openMenuUserModal}>
<div className="svg2">
<SVG src={downArrow} cacheGetRequests={true} />
</div>
</a>
</div>
</React.Fragment>
)
}
}
I come across this problem quite often and always do the following.
I mix css positioning and react hooks to create a modal. the overlayer div covers the whole div container so when you click any where in the container apart from the modal, the modal dissapears. z-index:1 on #modal makes sure the modal is stacked above the overlayer.
const Modal = () => {
const [modal, setModal] = React.useState(false);
return (
<div id='container'>
<button onClick={() => setModal(true)}>toggle modal</button>
{modal && <div id='overlayer' onClick={() => setModal(false)}></div>}
{modal && <div id='modal'>modal</div>}
</div>
);
};
ReactDOM.render(<Modal/>, document.getElementById("react"));
#container{
position: relative;
height: 200px; width:200px;
border: 1px solid black;
}
#container * {position: absolute;}
#overlayer{
height: 100%; width:100%;
}
#modal{
background: blue;
height: 30%; width:30%;
top: 12%; z-index: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
You should be able to call this.props.closeMenuUserModal() in your handleClick function.

How to have the children affect the parent in React?

I have a situation where I have something like this:
<DropDown>
<p>Select an option:</p>
<button onClick={() => console.log("opt 1")}>Option 1</button>
<button onClick={() => console.log("opt 1")}>Option 2</button>
</DropDown>
The DropDown component is one that I wrote, that renders this.props.children in a drop-down fashion. The DropDown has an onClick call that makes it close.
DropDown looks something like this (simplified):
class DropDown extends Component {
state = {
open: false
};
render() {
return (
<div className={`drop-down ${this.state.open ? "open" : "closed"}`}>
<div className="closed-version">
<div className="header" onClick={this.open}>
<div className="header-contents">Click here to select an option</div>
</div>
</div>
<div className="open-version">
<div className="content" onClick={this.closeWithoutSelection}>
{this.props.children}
</div>
</div>
</div>
);
}
open = () => {
this.setState({ open: true }, () => {
if (this.props.onOpen) {
this.props.onOpen();
}
});
};
closeWithoutSelection = () => {
this.setState({ open: false }, () => {
if (this.props.onCloseWithoutSelection) {
this.props.onCloseWithoutSelection();
}
});
};
}
The issue I'm running into is that I want to do something different to the DropDown whether it was closed selecting an option or not. How do I go about doing that?
In your DropDown component you have some state and you are probably rendering your children like this:
this.props.children
Instead you can use render props to pass state, methods or anything else down to your children without having to handle it outside of the DropDown component at the parent level.
class DropDown extends Component {
// constructor / state, methods...
yourSpecialMethod(item) {
// do your special thing here
}
render() {
// pass state or methods down to children!
return this.props.children(yourSpecialMethod)
}
}
Then modify your render slightly:
<DropDown>
{handleSpecialMethod =>
<>
<p>Select an option:</p>
<button onClick={() => handleSpecialMethod("opt 1")}>Option 1</button>
<button onClick={() => handleSpecialMethod("opt 1")}>Option 2</button>
</>
}
</DropDown>
UPDATED
Accordingly #pupeno (and he is right), the dropdown logic should be within the dropdown itself. However, we should pass a callback function in order to deal with the chosen data.
class DropDown extends React.Component {
state = {
open: false,
};
toggleDropdown = (e) => {
this.setState({
open: !this.state.open,
});
const value = e.target.getAttribute('value');
if ( value !== "null") {
this.props.selectItem(value);
}
};
render() {
const { selectItem } = this.props;
const { open } = this.state;
return (
<div className={open ? "drop-down open" : "drop-down"}>
<div onClick={this.toggleDropdown} value="null">Select</div>
<div onClick={this.toggleDropdown} value="1">Item 1</div>
<div onClick={this.toggleDropdown} value="2">Item 2</div>
<div onClick={this.toggleDropdown} value="3">Item 3</div>
</div>
);
}
};
class App extends React.Component {
requestItem = (item) => {
alert(`Request item ${item}`);
};
render() {
return (
<div>
<DropDown selectItem={this.requestItem}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.querySelector('#app')
);
.drop-down {
border: 1px solid black;
width: 180px;
height: 30px;
overflow: hidden;
transition: height 0.3s ease;
}
.open {
height: 120px;
}
.drop-down > div {
padding: 5px;
box-sizing: border-box;
height: 30px;
border-bottom: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
You could use an onChange function and call the parent function that is passed down as a prop. This will "affect the parent" as you asked.
this.props.onChange() or something similar.

Categories

Resources