I'm using the animate.css library with React and trying to set up a element (button) to pulse when hovered over. Tried to look through the docs and here but can't find a way to achieve this simple task. If anyone has achieved this or found a reference would greatly be appreciated.
class App extends Component {
constructor(props) {
super(props);
this.handleHover = this.handleHover.bind(this);
}
handleHover(){
this.setState({
isHovered: !this.state.isHovered
});
}
render() {
const btnClass = this.state.isHovered ? "pulse animated" : "";
return (
<div>
<button className={btnClass} onMouseEnter={this.state.handleHover} onMouseLeave={this.state.handleHover}>Test</button>
</div>
);
}
}
export default App;
You can use the onMouseEnter and onMouseLeave events on the component and toggle the class accordingly.
constructor(){
super();
this.state = {
isHovered: false
};
this.handleHover = this.handleHover.bind(this);
}
handleHover(){
this.setState(prevState => ({
isHovered: !prevState.isHovered
}));
}
render(){
const btnClass = this.state.isHovered ? "pulse animated" : "";
return <button className={btnClass} onMouseEnter={this.handleHover} onMouseLeave={this.handleHover}></button>
}
Update 05/07/19: Hooks
import React, { useState } from 'react';
export default function Component () {
const [hovered, setHovered] = useState(false);
const toggleHover = () => setHovered(!hovered);
return (
<button
className={hovered ? 'pulse animated' : ''}
onMouseEnter={toggleHover}
onMouseLeave={toggleHover}
>
</button>
)
}
What about using the css :hover property? This worked way better for me by changing my hover class section in the css file to use :hover instead of react.
I tried using the above suggestions but react didn't seem fast enough to get it so the state would become wrong if the mouse moved slowly over the button.
Related
I'm using Next js and react visibility sensor to let me know when a div is visible on screen.
Code kinda looks like:
import VisibilitySensor from "react-visibility-sensor";
function onChange(isVisible) {
let colorstate = isVisible ? "test" : "test dark";
console.log(colorstate)
}
export default function Home() {
return (
<VisibilitySensor onChange={onChange}>
<div className={colorstate}>this is a test div.</div>
</VisibilitySensor>
);
}
Changing the div className to the {colorstate} variable doesn't work (returns undefined).
I'm fairly new to React and I tried various answers online using "this.state" methods which all didn't work.
Right now the onChange function works fine and prints the correct class name in the log, I just don't know how to associate it with the div.
Thanks.
You can use useState hook, this is how it would look like with initial className of 'test dark'
import VisibilitySensor from "react-visibility-sensor";
import {useState} from 'react'
export default function Home() {
const [colorState, setColorState] = useState('test dark')
const onChange = (isVisible) => {
isVisible ? setColorState("test") : setColorState("test dark");
}
return (
<VisibilitySensor onChange={onChange}>
<div className={colorState}>this is a test div.</div>
</VisibilitySensor>
);
}
seems your colorState variable is visible only through the onChange.
class Home extends React.Component{
constructor(props){
super(props);
this.state =
{
dark: true
}
}
test = () => {
this.setState(
{
dark: !this.state.dark
}
)
}
render(){
return(
<div className={this.state.dark ? "dark" : "white"} onClick={this.test}>
test
</div>
);
}
}
should work
Background
I am trying to make an element disappear after the animation ends (I am using animate.css to create the animations).
The above 'copied' text uses animated fadeOut upon clicking the 'Copy to Journal Link'. Additionally, the above demo shows that it takes two clicks on the link to toggle the span containing the text 'copied' from displayed to not displayed.
According to the animate.css docs, one can also detect when an animation ends using:
const element = document.querySelector('.my-element')
element.classList.add('animated', 'bounceOutLeft')
element.addEventListener('animationend', function() { doSomething() })
My Problem
However, within the componentDidMount() tooltip is null when attempting to integrate what animate.css docs suggest.
What am I doing wrong? Is there a better way to handle this behavior?
ClipboardBtn.js
import React, { Component } from 'react'
import CopyToClipboard from 'react-copy-to-clipboard'
class ClipboardBtn extends Component {
constructor(props) {
super(props)
this.state = {
copied: false,
isShown: true,
}
}
componentDidMount() {
const tooltip = document.querySelector('#clipboard-tooltip')
tooltip.addEventListener('animationend', this.handleAnimationEnd)
}
handleAnimationEnd() {
this.setState({
isShown: false,
})
}
render() {
const { isShown, copied } = this.state
const { title, value } = this.props
return (
<span>
<CopyToClipboard onCopy={() => this.setState({ copied: !copied })} text={value}>
<span className="clipboard-btn">{title}</span>
</CopyToClipboard>
{this.state.copied ? (
<span
id="clipboard-tooltip"
className="animated fadeOut"
style={{
display: isShown ? 'inline' : 'none',
marginLeft: 15,
color: '#e0dbda',
}}
>
Copied!
</span>
) : null}
</span>
)
}
}
export default ClipboardBtn
Using query selectors in React is a big NO. You should NEVER do it. (not that that's the problem in this case)
But even though it's not the problem, it will fix your problem:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
https://reactjs.org/docs/refs-and-the-dom.html
componentDidMount gets called only once during the inital mount. I can see that in the inital component state, copied is false, hence #clipboard-tooltip never gets rendered. That is why tooltip is null.
Instead try this :
componentDidUpdate(prevProps, prevState) {
if(this.state.copied === true && prevState.copied === false) {
const tooltip = document.querySelector('#clipboard-tooltip')
tooltip.addEventListener('animationend', this.handleAnimationEnd)
}
if(this.state.copied === false && prevState.copied === true) {
const tooltip = document.querySelector('#clipboard-tooltip')
tooltip.removeEventListener('animationend', this.handleAnimationEnd)
}
}
componentDidUpdate gets called for every prop/state change and hence as soon as copied is set to true, the event handler is set inside componentDidUpdate. I have added a condition based on your requirement, so that it doesn't get executed everytime. Feel free to tweak it as needed.
i am making a website under react with reactstrap, i have a section that contains charts and a button whose function is to replace said charts with another chart containing more details. however i am struggling to make a concrete code.
i have tried placing the charts in a separate component and have it's content switch through the use of a handleclick function on the button that changes the state of the section (using 'onclick')
i am really not confident in my code's clarity, so i tried reproducing what i did in a simpler matter within fiddle
class hello extends React.Component {
render() {
return (
<h2>hello</h2>
);
}
}
class bye extends React.Component {
render() {
return (
<h2>goodbye</h2>
);
}
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<div>
<div>
{this.state.components[hello]}
</div>
<button onClick={this.handleClick}>
switch
{this.setState({components:[<bye />]})}
</button>
</div>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
the div in the "toggle" component is supposed to switch between the components "hello" and "bye"
in effect the current section that is supposed to be displayed ("hello") will be replaced by the other section ("bye") uppon clicking the button under them.
thanks in advance.
If you simply want to toggle between the two components with the button click, you can use conditional rendering.
Change your render method to this:
render(){
return (
<div>
{this.state.isToggleOn?<Hello />:<Bye />}
<button onClick={this.handleClick}>Switch</button>
</div>
}
Also keep your Component's name first character capitalized or react might complain. And using Class based Components is outdated. Hooks are the hot thing right now. So try to use more Functional Components.
Note: My answer assumes you are using babel presets for transpiling jsx and es6 syntax. If not, check out #Colin's answer. It also uses hooks.
why not import all partial views and conditionally render them based on the condition
{condition & <View1/>
There's a few mistakes in your code. Here's an example which does what you want using conditional rendering:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Hello = () => {
return <h2>hello</h2>;
};
const Bye = () => {
return <h2>bye</h2>;
};
const App = () => {
const [toggled, setToggled] = useState(true);
const handleClick = () => {
setToggled(!toggled);
};
const render = () => {
if (toggled) {
return <Hello />;
}
return <Bye />;
};
return (
<div>
<button onClick={handleClick}>toggle</button>
{render()}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
There are many ways to do it:
Using conditional operator:
{ this.state.isToggleOn?<Hello/>:<Bye/> }
Using if condition:
render() {
let chart;
if(this.state.isToggleOn) {
chart = <Hello/>;
} else {
chart = <Bye/>;
}
return ( <div> { chart } </div>);
}
3 You can use switch case also for conditional rendering. Here it is not well suited as condition is true or false.
You might have seen this type of effect. Example - https://codepen.io/anon/pen/GEmOQy
But I need to implement same way in React. I know I can use componentDidMount method for ajax, but thing is how to display response on hover.
I don't had practice on implementing hover with react, like I do in pure css approach with :hover.
So any solutions are welcome.
This a very flat question, and there are many possibilities. All I can give is a basic skeleton on how to do it.
Define a ImageCard component in whose componentDidMount you do the API call. Then on your parent component ( whichevere component the button is ), store a state key that manages whether to show the card or not:
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
showCard: false
};
}
render () {
return (
<div>
{
this.state.showCard &&
<ImageCard/>
}
<button
onMouseEnter={() => this.setState({ showCard: true })}
onMouseLeave={() => this.setState({ showCard: false })}
>Hover Me!</button>
</div>
)
}
}
Use onMouseEnter event on the button and load a new image when it fires,
class SomeComp extends Component{
coonstructor(){
this.state = {
url: 'http://some-intial-url.com'
}
this.onMouseEnter = this.onMouseEnter.bind(this)
}
onMouseEnter(){
this.setState({
url: GetOtherImageURl()
})
}
render(){
<img src = {this.state.url} />
}
}
I'm trying to hide an image by default and only show it when element is hovered. I've been able to set the default state etc.. Only issue is creating an if statement that will show and hide the image.
This is the component:
import React from 'react';
import { Link } from 'react-router';
import Eyecon from '../../static/eye.svg';
class Item extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Item';
this.handleHover = this.handleHover.bind(this);
this.state = {
hover: false
};
}
mouseOver() {
this.state.hover = true;
}
mouseOut() {
this.state.hover = false;
}
handleHover() {
console.log("hover");
}
render() {
const { item, i } = this.props;
return (
<div className="grid-box">
<img src={Eyecon}/>
</div>
)
}
}
export default Item;
I've tried a few things, but also want to see what the best practice is.
Thanks for your time
There are multiple ways, I would do it like this:
render() {
const { item, i } = this.props;
return (
<div className="grid-box">
{this.state.hover ? (
<img src={Eyecon} />
) : null}
</div>
)
}
But you could also abstract the image rendering into a separate function and not return anything when needed.
Sitenote: You shouldn't mutate the state directly. Use the this.setState() function. Otherwise the component will not be re-rendered.
Also, may I ask why you're not just using css :hover to achieve this behaviour?
I typically like to handle conditional displaying of content in helper functions, like so:
function renderImage() {
const { hover } = this.state;
if (hover) {
return (
<img src={Eyecon} />
);
}
}
Then, you can just call this function from render()
render() {
const { item, i } = this.props;
return (
<div className="grid-box">
{renderImage.call(this)}
</div>
)
}