Can I Fastclick ReactJS running in Cordova

时光总嘲笑我的痴心妄想 提交于 2019-11-26 11:48:23

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.

David Hellsing

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');
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!