react-native 关于触控事件节流,控制点击事件触发频率

  • 解决问题

    做react-native过程中,由于代码响应,会出现点击一个按钮响应时间过程(或者快速点击多次情况),用户多次点击的情况。
    如果你该按钮对应事件为路由导航,此时路由中就会出现多次此路由。
    在开发过程中 为了解决这个问题 实现方案为 控制点击频率
    
    这里实现两种效果
    	单个组件实现控制节流
    	整个App所有点击事件实现节流
    
  • 单个组件实现控制节流

    • 思路

      大部分触控事件都是有四大组件来实现

        * TouchableNativeFeedback
        * TouchableHighlight
        * TouchableOpacity
        * TouchableWithoutFeedback
      

      思考之后的解决方案大致可以为两种

        * 在原型链上进行点击事件的控制
        * 对四个组件进行包装 整个项目使用自定义组件替代系统提供的点击组件 
      
    • 方案选择

      观察源码 并且查看四大控件的继承关系。不能方便的进行一次性改变原型继承和增加方法来控制点击频率
      
      选择进行对四大控件进行包装 提供给全局使用
      
    • 代码实现

      //任务类 控制事件触发间隔
      class ThrottleTask {
          throttle = 0
          timer = null
          constructor(time = 1000) {
              this.throttle = Math.abs(time)
          }
          //开始计时
          start() { this.timer = setTimeout(this.end.bind(this), this.throttle) }
          //结束计时
          end() {
              if (this.timer != null) {
                  clearTimeout(this.timer)
                  this.timer = null;
              }
          }
          //判断任务类的运行
          isRuning() { return this.timer != null; }
      }
      
      //节流控制类型  (时间为基准 或 任务为基准)
      export const TouchableType = {
          Throttle: "Throttle",
          Debounce: "Debounce"
      }
      
      //这里选择HOC高阶组件对原生组件进行包装
      export function ConnectTouchable(config = {}) {
          return function (WrapComponent) {
              class TouchableThrottle extends Component {
                  throttleTask = null;
                  constructor(props) {
                      super(props);
                      //属性配置
                      /**
                      * 1:节流时间
                      * 2:节流类型
                      * 3:是否该组件需要节流
                      */
                      this.state = {
                          throttle: this.props.throttle || config.throttle || 1000,
                          type: this.props.type || config.type || TouchableType.Throttle,
                          isthrottle: this.props.isthrottle || config.isthrottle || true
                      }
                      //创建任务类
                      if (this.state.isthrottle)
                          this.throttleTask = new ThrottleTask(this.state.throttle)
                  }
                  componentWillReceiveProps(nP, op) {
                      this.setState({
                          type: nP.type,
                          isthrottle: nP.isthrottle
                      });
                  }
                  reStartTask() {
                  		//节流任务重新计时
                      this.throttleTask.end()
                      this.throttleTask.start()
                  }
                  onPress = (...args) => {
      	            //对默认的点击事件进行处理
                      if (!this.state.isthrottle) { this.props.onPress && this.props.onPress(...args); return; }
                      if (this.throttleTask.isRuning()) {
                          if (this.state.type == TouchableType.Debounce)
                          	//重启定时任务
                              this.reStartTask()
                          return
                      } else {
      	                	//重启定时任务
                          this.reStartTask()
                          //触发时间
                          this.props.onPress && this.props.onPress(...args)
                      }
                  }
                  componentWillUnmount() {
                      this.throttleTask.end()
                      this.throttleTask = null;
                  }
                  render() {
                      return (
                          <WrapComponent {...this.props} onPress={this.onPress}>
                              {
                                  this.props.children
                              }
                          </WrapComponent>
                      )
                  }
              }
              //默认属性
              TouchableThrottle.defaultProps = {
                  throttle: 500,//节流时间为500ms
                  type: TouchableType.Throttle,//是时间为准 500ms只能容许一次点击
                  isthrottle: true //默认开启节流控制
              }
              TouchableThrottle.propTypes = {
                  throttle: PropTypes.number,
                  type: PropTypes.string,
                  isthrottle: PropTypes.bool
              }
      
              TouchableThrottle.WrappedComponent = WrapComponent
              TouchableThrottle.displayName = `Throttle${WrapComponent.displayName}`
              //import hoistNonReactStatics from "hoist-non-react-statics";
              //解决 static 变量问题
              return hoistNonReactStatics(TouchableThrottle, WrapComponent)
          }
      }
      
      //对系统组件进行包装 导出提供使用
      export const TouchableNativeFeedbackThrottle = ConnectTouchable({ throttle: 500, type: TouchableType.Throttle, isthrottle: true })(TouchableNativeFeedback)
      export const TouchableHighlightThrottle = ConnectTouchable({ throttle: 500, type: TouchableType.Throttle, isthrottle: true })(TouchableHighlight)
      export const TouchableOpacityThrottle = ConnectTouchable({ throttle: 500, type: TouchableType.Throttle, isthrottle: true })(TouchableOpacity)
      export const TouchableWithoutFeedbackThrottle = ConnectTouchable({ throttle: 500, type: TouchableType.Throttle, isthrottle: true })(TouchableWithoutFeedback)
      
      //使用 替代方便 无需过多配置
      	<TouchableOpacity></TouchableOpacity> 
      	直接替换为
      	<TouchableOpacityThrottle></TouchableOpacityThrottle>  
      	<TouchableOpacityThrottle throttle= {500} type = {"Debounce"} isthrottle= {true}>
      
  • 对整个App所有点击事件 进行 节流控制

    • 思路
    要对所有点击事件进行控制,就不能使用包装类。因为无法保证第三方库是否使用系统触控组件,这样就不能达到实现目的。
    
    只能对系统四种触控组件进行原型修改。来达到目的	
    
    • 方案选择

      TouchableOpacity = createReactClass({
        	displayName: 'TouchableOpacity',
      	mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin]
      	.......
      }
      
      TouchableWithoutFeedback = createReactClass({
        displayName: 'TouchableWithoutFeedback',
        mixins: [TimerMixin, Touchable.Mixin],
        ........
      }
      
      TouchableHighlight = createReactClass({
        displayName: 'TouchableHighlight',
        mixins: [NativeMethodsMixin, Touchable.Mixin],
        .......
      }
      
      TouchableNativeFeedback = createReactClass({
        displayName: 'TouchableNativeFeedback',
        mixins: [Touchable.Mixin],
        	......
      }
      
      
      查看四大控件的源码可以知道 四大组件组件都必须满足  mixins: [Touchable.Mixin] 条件
      	[Mixin]不大了解的 找找度娘
      继续查看TouchableMixin源码 会发现 所有点击的事件的源头 是
      	touchableHandlePress 方法
      总结方案
      	重写四大组件的touchableHandlePress方案达到控制节流目的
      
    • 方案实现

      import React, { Component } from 'react';
      import { StyleSheet, TouchableHighlight, TouchableNativeFeedback, TouchableOpacity, TouchableWithoutFeedback } from 'react-native';
      //记录上一次事件触发时间
      let LastTouchTime = 0;
      //节流时间
      let intervalTime = 700;
      //高阶组件控制
      function AppHandleAppToubleTime(TouchComponent) {
          const old_Touchable_ComponentDidMount = TouchComponent.prototype.componentDidMount;
          //在声明周期函数中 替换 事件源头事件touchableHandlePress 
          TouchComponent.prototype.componentDidMount = function (...args) {
              if (this.props.onPress) {
                  let Touchable_touchableHandlePress = this.touchableHandlePress;
                  /**
                   * 替换系统默认点击事件
                   * @param {*} _args 
                   */
                  this.touchableHandlePress = function (..._args) {
                      if (this.props.throttle == false) {
                          Touchable_touchableHandlePress(..._args);
                          LastTouchTime = (new Date()).getTime();
                      } else {
                          let now = (new Date()).getTime();
                          if ((now - LastTouchTime) > intervalTime) {
                              Touchable_touchableHandlePress(..._args);
                              LastTouchTime = now;
                          }
                      }
      
                  }
              }
              old_Touchable_ComponentDidMount.bind(this)(...args);
          }
      }
      
      //处理系统触控组件
      AppHandleAppToubleTime(TouchableHighlight);
      AppHandleAppToubleTime(TouchableOpacity);
      AppHandleAppToubleTime(TouchableWithoutFeedback);
      AppHandleAppToubleTime(TouchableNativeFeedback);
      
      //原有代码不需要做任何修改 
      <TouchableOpacity></TouchableOpacity>
      //关闭这个组件的节流控制
      <TouchableOpacity throttle={false}></TouchableOpacity>
      
  • 总结

    • 多多查看源码 会发现不一样的天地
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值