score:18
Edit:
I've found that this is not an issue with React 0.15.3.
Original:
For React 0.13.3, here are two solutions.
1. ref callback
Note, even in the case of double-click, the single-click handler will be called twice (once for each click).
const ListItem = React.createClass({
handleClick() {
console.log('single click');
},
handleDoubleClick() {
console.log('double click');
},
refCallback(item) {
if (item) {
item.getDOMNode().ondblclick = this.handleDoubleClick;
}
},
render() {
return (
<div onClick={this.handleClick}
ref={this.refCallback}>
</div>
);
}
});
module.exports = ListItem;
2. lodash debounce
I had another solution that used lodash
, but I abandoned it because of the complexity. The benefit of this was that "click" was only called once, and not at all in the case of "double-click".
import _ from 'lodash'
const ListItem = React.createClass({
handleClick(e) {
if (!this._delayedClick) {
this._delayedClick = _.debounce(this.doClick, 500);
}
if (this.clickedOnce) {
this._delayedClick.cancel();
this.clickedOnce = false;
console.log('double click');
} else {
this._delayedClick(e);
this.clickedOnce = true;
}
},
doClick(e) {
this.clickedOnce = undefined;
console.log('single click');
},
render() {
return (
<div onClick={this.handleClick}>
</div>
);
}
});
module.exports = ListItem;
on the soapbox
I appreciate the idea that double-click isn't something easily detected, but for better or worse it IS a paradigm that exists and one that users understand because of its prevalence in operating systems. Furthermore, it's a paradigm that modern browsers still support. Until such time that it is removed from the DOM specifications, my opinion is that React should support a functioning onDoubleClick
prop alongside onClick
. It's unfortunate that it seems they do not.
score:0
This is the solution of a like button with increment and discernment values based on solution of Erminea.
useEffect(() => {
let singleClickTimer;
if (clicks === 1) {
singleClickTimer = setTimeout(
() => {
handleClick();
setClicks(0);
}, 250);
} else if (clicks === 2) {
handleDoubleClick();
setClicks(0);
}
return () => clearTimeout(singleClickTimer);
}, [clicks]);
const handleClick = () => {
console.log('single click');
total = totalClicks + 1;
setTotalClicks(total);
}
const handleDoubleClick = () => {
console.log('double click');
if (total > 0) {
total = totalClicks - 1;
}
setTotalClicks(total);
}
return (
<div
className="likeButton"
onClick={() => setClicks(clicks + 1)}
>
Likes | {totalClicks}
</div>
)
score:0
Here is one way to achieve the same with promises. waitForDoubleClick
returns a Promise
which will resolve only if double click was not executed. Otherwise it will reject. Time can be adjusted.
async waitForDoubleClick() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
if (!this.state.prevent) {
resolve(true);
} else {
reject(false);
}
}, 250);
this.setState({ ...this.state, timeout, prevent: false })
});
}
clearWaitForDoubleClick() {
clearTimeout(this.state.timeout);
this.setState({
prevent: true
});
}
async onMouseUp() {
try {
const wait = await this.waitForDoubleClick();
// Code for sinlge click goes here.
} catch (error) {
// Single click was prevented.
console.log(error)
}
}
score:0
Here's my solution for React in TypeScript:
import { debounce } from 'lodash';
const useManyClickHandlers = (...handlers: Array<(e: React.UIEvent<HTMLElement>) => void>) => {
const callEventHandler = (e: React.UIEvent<HTMLElement>) => {
if (e.detail <= 0) return;
const handler = handlers[e.detail - 1];
if (handler) {
handler(e);
}
};
const debounceHandler = debounce(function(e: React.UIEvent<HTMLElement>) {
callEventHandler(e);
}, 250);
return (e: React.UIEvent<HTMLElement>) => {
e.persist();
debounceHandler(e);
};
};
And an example use of this util:
const singleClickHandler = (e: React.UIEvent<HTMLElement>) => {
console.log('single click');
};
const doubleClickHandler = (e: React.UIEvent<HTMLElement>) => {
console.log('double click');
};
const clickHandler = useManyClickHandlers(singleClickHandler, doubleClickHandler);
// ...
<div onClick={clickHandler}>Click me!</div>
score:0
I've updated Erminea Nea solution with passing an original event so that you can stop propagation + in my case I needed to pass dynamic props to my 1-2 click handler. All credit goes to Erminea Nea.
Here is a hook I've come up with:
import { useState, useEffect } from 'react';
const initialState = {
click: 0,
props: undefined
}
function useSingleAndDoubleClick(actionSimpleClick, actionDoubleClick, delay = 250) {
const [state, setState] = useState(initialState);
useEffect(() => {
const timer = setTimeout(() => {
// simple click
if (state.click === 1) actionSimpleClick(state.props);
setState(initialState);
}, delay);
// the duration between this click and the previous one
// is less than the value of delay = double-click
if (state.click === 2) actionDoubleClick(state.props);
return () => clearTimeout(timer);
}, [state.click]);
return (e, props) => {
e.stopPropagation()
setState(prev => ({
click: prev.click + 1,
props
}))
}
}
export default useSingleAndDoubleClick
Usage in some component:
const onClick = useSingleAndDoubleClick(callbackClick, callbackDoubleClick)
<button onClick={onClick}>Click me</button>
or
<button onClick={e => onClick(e, someOtherProps)}>Click me</button>
score:0
import React, { useState } from "react";
const List = () => {
const [cv, uv] = useState("nice");
const ty = () => {
uv("bad");
};
return (
<>
<h1>{cv}</h1>
<button onDoubleClick={ty}>Click to change</button>
</>
);
};
export default List;
score:5
Here's what I have done. Any suggestions for improvement are welcome.
class DoubleClick extends React.Component {
state = {counter: 0}
handleClick = () => {
this.setState(state => ({
counter: this.state.counter + 1,
}))
}
handleDoubleClick = () => {
this.setState(state => ({
counter: this.state.counter - 2,
}))
}
render() {
return(
<>
<button onClick={this.handleClick} onDoubleClick={this.handleDoubleClick>
{this.state.counter}
</button>
</>
)
}
}
score:25
Instead of using ondoubleclick
, you can use event.detail
to get the current click count. It's the number of time the mouse's been clicked in the same area in a short time.
const handleClick = (e) => {
switch (e.detail) {
case 1:
console.log("click");
break;
case 2:
console.log("double click");
break;
case 3:
console.log("triple click");
break;
}
};
return <button onClick={handleClick}>Click me</button>;
In the example above, if you triple click the button it will print all 3 cases:
click
double click
triple click
Live Demo
score:26
You can use a custom hook to handle simple click and double click like this :
import { useState, useEffect } from 'react';
function useSingleAndDoubleClick(actionSimpleClick, actionDoubleClick, delay = 250) {
const [click, setClick] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
// simple click
if (click === 1) actionSimpleClick();
setClick(0);
}, delay);
// the duration between this click and the previous one
// is less than the value of delay = double-click
if (click === 2) actionDoubleClick();
return () => clearTimeout(timer);
}, [click]);
return () => setClick(prev => prev + 1);
}
then in your component you can use :
const click = useSingleAndDoubleClick(callbackClick, callbackDoubleClick);
<button onClick={click}>clic</button>
score:34
The required result can be achieved by providing a very slight delay on firing off the normal click action, which will be cancelled when the double click event will happen.
let timer = 0;
let delay = 200;
let prevent = false;
doClickAction() {
console.log(' click');
}
doDoubleClickAction() {
console.log('Double Click')
}
handleClick() {
let me = this;
timer = setTimeout(function() {
if (!prevent) {
me.doClickAction();
}
prevent = false;
}, delay);
}
handleDoubleClick(){
clearTimeout(timer);
prevent = true;
this.doDoubleClickAction();
}
< button onClick={this.handleClick.bind(this)}
onDoubleClick = {this.handleDoubleClick.bind(this)} > click me </button>
score:71
This is not a limitation of React, it is a limitation of the DOM's click
and dblclick
events. As suggested by Quirksmode's click documentation:
Don't register click and dblclick events on the same element: it's impossible to distinguish single-click events from click events that lead to a dblclick event.
For more current documentation, the W3C spec on the dblclick
event states:
A user agent must dispatch this event when the primary button of a pointing device is clicked twice over an element.
A double click event necessarily happens after two click events.
Edit:
One more suggested read is jQuery's dblclick
handler:
It is inadvisable to bind handlers to both the click and dblclick events for the same element. The sequence of events triggered varies from browser to browser, with some receiving two click events before the dblclick and others only one. Double-click sensitivity (maximum time between clicks that is detected as a double click) can vary by operating system and browser, and is often user-configurable.
Source: stackoverflow.com
Related Query
- onClick works but onDoubleClick is ignored on React component
- React onClick works on reload but not on actual click
- Axios not updating state from API in function called on Click, but works on identical function on Component Mount in React
- Why React component is launching onClick event on render, but not when clicked?
- onClick affecting another element but same react component
- My react onClick navigate shows URL, but does not render component
- Failure to bundle a React component library via Webpack, but it works via Babel
- React component works fine, but attempting to use useMemo() results in an error
- Request to api works fine in component but not when using provider with react
- button onClick works only once in react component
- Introduced pagination into my react app. Works fine but getting some slowdown and unresponsive in-app navigation. Unmounted component warning
- React component not rendering in production build but works as expected in development build
- socket connect event inside useEffect does not work when component is created but works after refreshing the page in React
- React Handling component onClick (one opens, but another closes)
- React Component CSS does not work when digit zero "0" is displayed but works fine with other values
- Why is `Promise.then` called twice in a React component but not the console.log?
- React onClick event on component
- React Routing works in local machine but not Heroku
- A React component suspended while rendering, but no fallback UI was specified
- Why do I have to .bind(this) for methods defined in React component class, but not in regular ES6 class
- React ES6 component inheritance: working, but not recommended?
- React Redux Store updating, but component not re-rendering
- Setting conditional onClick behaviour in React Component
- React native Refresh works but next call still uses the last token
- React navigation didfocus event listener works differently between class component and functional component
- React setState not working on first try, but works on second?
- React app works on Chrome, but not Firefox
- how should I build onClick action in my react component + Redux
- Handle React onDoubleClick and single onClick for same element
- How to make a React component fade in on scroll using IntersectionObserver, but only once?
More Query from same tag
- React - Passing fetched data from API as props to components
- React Js axios returns empty div before actual data
- How do I add a Babel plugin to a create-react-app project?
- Convert a series of If to R.cond correctly
- Conditional CSS Reactjs in Tab Navigation
- How to use Stencil's `EventEmitter` inside React JSX?
- Remove Y Axis and its grid lines
- React.semantic ui's Icones are not loading with webpack middle ware
- A component suspended while responding to synchronous input
- Is there a way to play a music with <audio> in react?
- Using date from React query to filter existing state causing too many re-renders
- How to access element in array object [API]
- React Hooks clear inputs after submit
- npm start refuses to work after adding React-Bootstrap
- Mapping JavaScript Object property with another property in same JavaScript Object
- Pass in a <li> when there is data with ternary operator in React
- can't read else part
- Recompose "withReducer": justification of async reducer function call
- nextjs Dynamic route rendering content not working
- How to animate a conditionally rendered part of the component on state change using javascript and css?
- How do I open and close a Material-UI Dialog in Meteor/React?
- Using CSS module class in a function, React
- Capturing grid item click event in react-grid-layout
- Using a button to change the color of text
- React Material UI DataGrid: Cannot read property 'useRef' of undefined
- Change the value of all the fields in an object using useState hook
- How to add height and overflow properties to react table in bootstrap react?
- React — componentDidUpdate as a promise/callback. Is it possible?
- I am building an e-commerce site using ReactJS I'm trying to get the correct quantity from my select tag to appear in my cart?
- how to configure web service url in react app deployed on heroku to access java REST web services which is also deployed in heroku