简体   繁体   中英

Can I Fastclick ReactJS running in Cordova

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 .

\n

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.

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.

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');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM