-
解决问题
做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>
-
总结
- 多多查看源码 会发现不一样的天地
react-native 关于触控事件节流,控制点击事件触发频率
最新推荐文章于 2024-09-09 22:26:33 发布