Does fastclick work with ReactJS's event system? It doesn't seem to be taking when run through Cordova onto iOS or Android. If not, is there another way of getting the same results. My app has no double-tap functionality so I'd like to remove that delay across the board, if possible...
Edit
Facebook decided to not add support for defining custom event types and recommend you to use something like react-tappable so you can write something like <Tappable onTap={}>
.
Facebook's working on a solution in the form of TapEventPlugin
, but it won't be made available until they make some decisions.
If you're reading this you're probably working on a project that can't wait until they figure out how they want to publish it.
This repo is for you: https://github.com/zilverline/react-tap-event-plugin
When Facebook solves #436 and #1170, this repo will disappear.
This solution works for React 0.14.x and 15.x.
npm i -S react-tap-event-plugin
Example of usage:
var React = require("react");
var ReactDOM = require("react-dom");
injectTapEventPlugin = require("react-tap-event-plugin");
injectTapEventPlugin();
var Main = React.createClass({
render: function() {
return (
<a
href="#"
onTouchTap={this.handleTouchTap}
onClick={this.handleClick}>
Tap Me
</a>
);
},
handleClick: function(e) {
console.log("click", e);
},
handleTouchTap: function(e) {
console.log("touchTap", e);
}
});
ReactDOM.render(<Main />, document.getElementById("container"));
Note that with the injector, you will probably need to use only onTouchTap
(and not onClick
anymore).
I got FastClick to work with React, in a Webpack project. A few things seem finicky but it mostly works. (Update: only a toggle switch that was simulating clicks on a hidden checkbox was finicky -- that would be a problem regardless of React). Here's how I turned it on:
npm install -S fastclick
In main.jsx
:
import FastClick from 'fastclick';
window.addEventListener('load', () => {
FastClick.attach(document.body);
});
So even if you're not using Webpack or Browserify, I'm guessing as long as you can run the load event listener, you'll be fine.
new webpack.NormalModuleReplacementPlugin(/fastclick$/i, 'node-noop')
–
Hoodoo onClick
handlers. –
Pancreas onClick
prop but not passing it down to the component whatever render()
returns, so that it ultimately winds up on a real HTML component? As far as I know, an onClick
handler on a React component won't do anything, you have to pass it down to an HTML element. –
Pancreas We recently created a React component that is similar to fastclick, except that it’s much simpler and requires a manual callback. It’s pretty short so I’ll post it here:
React.initializeTouchEvents(true)
var TouchClick = React.createClass({
defaults: {
touched: false,
touchdown: false,
coords: { x:0, y:0 },
evObj: {}
},
getInitialState: function() {
return this.defaults
},
handler: function() {
typeof this.props.handler == 'function' && this.props.handler.apply(this, arguments)
},
getCoords: function(e) {
if ( e.touches && e.touches.length ) {
var touch = e.touches[0]
return {
x: touch.pageX,
y: touch.pageY
}
}
},
onTouchStart: function(e) {
this.setState({
touched: true,
touchdown: true,
coords: this.getCoords(e),
evObj: e
})
},
onTouchMove: function(e) {
var coords = this.getCoords(e)
var distance = Math.max(
Math.abs(this.state.coords.x - coords.x),
Math.abs(this.state.coords.y - coords.y)
)
if ( distance > 6 )
this.setState({ touchdown: false })
},
onTouchEnd: function() {
if(this.state.touchdown)
this.handler.call(this, this.state.evObj)
setTimeout(function() {
if ( this.isMounted() )
this.setState(this.defaults)
}.bind(this), 4)
},
onClick: function() {
if ( this.state.touched )
return false
this.setState(this.defaults)
this.handler.apply(this, arguments)
},
render: function() {
var classNames = ['touchclick']
this.props.className && classNames.push(this.props.className)
this.state.touchdown && classNames.push('touchdown')
return React.DOM[this.props.nodeName || 'button']({
className: classNames.join(' '),
onTouchStart: this.onTouchStart,
onTouchMove: this.onTouchMove,
onTouchEnd: this.onTouchEnd,
onClick: this.onClick
}, this.props.children)
}
})
Just pass handler
prop as the callback and wrap your content inside. This also works for systems that has both touch and click events (like newer windows 8 laptops). Example:
<TouchClick handler={this.clickHandler} className='app'>
<h1>Hello world</h1>
</TouchClick>
I had issues using David`s method, so as an alternative to FastClick, I implemented a mixin using HammerJs for the event. A bit more code to setup the event, but works fine.
var HammerClickMixin = React.createClass({
componentWillMount: function() {
this.listeneres = [];
},
addTapEvent: function(element,callback) {
var mc = new Hammer.Manager(element);
mc.add(new Hammer.Tap({ event: 'doubletap', taps: 2 }));
mc.add(new Hammer.Tap({ event: 'tap', taps: 1 }));
mc.on('tap',callback);
this.listeneres.push(mc);
},
addDoubleTap : function(element,callback){
var mc = new Hammer.Manager(element);
mc.add(new Hammer.Tap({ event: 'doubletap', taps: 2 }));
mc.on('doubletap',callback);
this.listeneres.push(mc);
},
componentWillUnmount: function() {
for(var i= 0; i < this.listeneres.length; i++){
this.listeneres[i].destroy();
}
}
});
This can then be used as as followed:
var Component = React.createClass({
mixins: [HammerClickMixin],
componentDidMount: function () {
this.addTapEvent(this.refs.elementToClick.getDOMNode(),function(){
//Handle fast hammer tap!
});
},
render: function () {
return (
<div ref="elementToClick"/>
);
}
});
It seemed to be working fine in my Cordova app, but there is one significant problem I ran into.
When an element is clicked using React+FastClick, and the next rendered view contains a clickable element in the same position, a onTouchEnd event is also registered in the second element.
I ditched FastClick as I don't want to align my buttons to avoid unwanted behaviour, but I need something to replace it as the click delay feels pretty bad.
onTouchEnd
issue you were seeing? –
Pancreas You can also use react-fastclick
(https://github.com/JakeSidSmith/react-fastclick) from npm:
npm i react-fastclick --save
Using it you don't have to change any of your code and it works really nice! You only have to require it once.
require('react-fastclick');
© 2022 - 2024 — McMap. All rights reserved.