使用react render props实现倒计时的示例代码


Posted in Javascript onDecember 06, 2018

react的组件模式可以观看Michael Chan的演讲视频,平时大家常听到的react模式也是HOC, HOC的使用场景很多,譬如react-redux的connect,这里不赘述HOC相关,感兴趣可以自行了解。

首先是这样一个场景,我的业务需要实现倒计时,倒计时你懂得,倒计时经常应用在预告一个活动的开始,像秒杀,像开售抢购等,或者活动的截止。

使用react render props实现倒计时的示例代码

我们来梳理一下这个倒计时的功能:

  • 定时更新时间,以秒为度;
  • 可以更新倒计时的截止时间,比如从10月1日更新为10月2日;
  • 倒计时结束,执行对应结束逻辑;
  • 倒计时结束,开启另一个活动倒计时;
  • 同时有多个倒计时;

这个时候我便开始编码,考虑代码复用,我用Class的模式实现一个倒计时:

class Timer {
 constructor(time, countCb, timeoutCb) {
  this.countCb = countCb;
  this.timeoutCb = timeoutCb;
  this.setDelayTime(time);
 }

 intervalId = null;

 clearInterval = () => {
  if (this.intervalId) {
   clearInterval(this.intervalId);
  }
 }

 // 更新倒计时的截止时间
 setDelayTime = (time) => {
  this.clearInterval();

  if (time) {
   this.delayTime = time;
   this.intervalId = setInterval(() => {
    this.doCount();
   }, 1000);
  }
 }

 doCount = () => {
  const timeDiffSecond =
   `${this.delayTime - Date.now()}`.replace(/\d{3}$/, '000') / 1000;

  if (timeDiffSecond <= 0) {
   this.clearInterval();
   if (typeof this.timeoutCb === 'function') {
    this.timeoutCb();
   }
   return;
  }

  const day = Math.floor(timeDiffSecond / 86400);
  const hour = Math.floor((timeDiffSecond % 86400) / 3600);
  const minute = Math.floor((timeDiffSecond % 3600) / 60);
  const second = Math.floor((timeDiffSecond % 3600) % 60);

  // 执行回调,由调用方决定显示格式
  if (typeof this.countCb === 'function') {
   this.countCb({
    day,
    hour,
    minute,
    second,
   });
  }
 }
}

export default Timer;

通过class的方式可以实现我的上述功能,将格式显示交给调用方决定,Timer只实现倒计时功能,这并没有什么问题,我们看调用方如何使用:

// 这是一个react组件部分代码 
 componentDidMount() {
  // 开启倒计时
  this.countDownLiveDelay();
 }

 componentDidUpdate() {
  // 开启倒计时
  this.countDownLiveDelay();
 }

 componentWillUnmount() {
  if (this.timer) {
   this.timer.clearInterval();
  }
 }

 timer = null;

 countDownLiveDelay = () => {
  const {
   countDownTime,
   onTimeout,
  } = this.props;

  if (this.timer) { return; }

  const time = countDownTime * 1000;

  if (time <= Date.now()) {
   onTimeout();
  }
  // new 一个timer对象
  this.timer = new Timer(time, ({ hour, minute, second }) => {
   this.setState({
    timeDelayText: `${formateTimeStr(hour)}:${formateTimeStr(minute)}:${formateTimeStr(second)}`,
   });
  }, () => {
   this.timer = null;

   if (typeof onTimeout === 'function') {
    onTimeout();
   }
  });
 }

 render() {
  return (
   <span style={styles.text}>{this.state.timeDelayText}</span>
  );
 }

查看这种方式的调用的缺点:调用方都需要手动开启倒计时,countDownLiveDelay方法调用

总感觉不够优雅,直到我看到了react的render props, 突然灵关一现,来了下面这段代码:

let delayTime;
// 倒计时组件
class TimeCountDown extends Component {
 state = {
  day: 0,
  hour: 0,
  minute: 0,
  second: 0,
 }

 componentDidMount() {
  delayTime = this.props.time;
  this.startCountDown();
 }

 componentDidUpdate() {
  if (this.props.time !== delayTime) {
   delayTime = this.props.time;

   this.clearTimer();
   this.startCountDown();
  }
 }

 timer = null;

 clearTimer() {
  if (this.timer) {
   clearInterval(this.timer);
   this.timer = null;
  }
 }

 // 开启计时
 startCountDown() {
  if (delayTime && !this.timer) {
   this.timer = setInterval(() => {
    this.doCount();
   }, 1000);
  }
 }

 doCount() {
  const {
   onTimeout,
  } = this.props;

  // 使用Math.floor((delayTime - Date.now()) / 1000)的话会导致这里值为0,前面delayTime - Date.now() > 0
  const timeDiffSecond = (delayTime - `${Date.now()}`.replace(/\d{3}$/, '000')) / 1000;

  if (timeDiffSecond <= 0) {
   this.clearTimer();
   if (typeof onTimeout === 'function') {
    onTimeout();
   }
   return;
  }

  const day = Math.floor(timeDiffSecond / 86400);
  const hour = Math.floor((timeDiffSecond % 86400) / 3600);
  const minute = Math.floor((timeDiffSecond % 3600) / 60);
  const second = Math.floor((timeDiffSecond % 3600) % 60);

  this.setState({
   day,
   hour,
   minute,
   second,
  });
 }

 render() {
  const {
   render,
  } = this.props;

  return render({
   ...this.state,
  });
 }
}

export default TimeCountDown;

具体TimeCountDown代码可戳这里

调用方:

import TimeCountDown from 'TimeCountDown';
function formateTimeStr(num) {
 return num < 10 ? `0${num}` : num;
}
// 业务调用倒计时组件
class CallTimer extends Component {
 onTimeout = () => {
  this.forceUpdate();
 }
 render() {
  // 传递render函数
  return (
   <span style={styles.statusText}>
    距直播还有
    <TimeCountDown
      time={time}
      onTimeout={() => { this.onTimeout(); }}
      render={({ hour, minute, second }) => {
       return (
        <span>
         {formateTimeStr(hour)}:{formateTimeStr(minute)}:{formateTimeStr(second)}
        </span>
       );
      }}
     />
      </span>
  )
 }
}

对比这种方式,通过传递一个函数render方法给到TimeCountDown组件,TimeCountDown组件渲染时执行props的render方法,并传递TimeCountDown的state进行渲染,这就是render props的模式了,这种方式灵活、优雅很多,很多场景都可以使用这种方式,而无需使用HOC。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
解决AJAX中跨域访问出现'没有权限'的错误
Aug 20 Javascript
javascript中直接写php代码的方法
Jul 31 Javascript
jquery如何通过name名称获取当前name的value值
Dec 20 Javascript
jquery实现更改表格行顺序示例
Apr 30 Javascript
Node.js中child_process实现多进程
Feb 03 Javascript
JQuery页面地址处理插件jqURL详解
May 03 Javascript
jquery实现简单的自动播放幻灯片效果
Jun 13 Javascript
bootstrap-datetimepicker实现只显示到日期的方法
Nov 25 Javascript
JavaScript中的FileReader图片预览上传功能实现代码
Jul 24 Javascript
jq.ajax+php+mysql实现关键字模糊查询(示例讲解)
Jan 02 Javascript
Vue实现自定义下拉菜单功能
Jul 16 Javascript
Vue 电商后台管理项目阶段性总结(推荐)
Aug 22 Javascript
微信小程序冒泡事件及其阻止方法实例分析
Dec 06 #Javascript
谈谈React中的Render Props模式
Dec 06 #Javascript
详解Vue-axios 设置请求头问题
Dec 06 #Javascript
超好用的jQuery分页插件jpaginate用法示例【附源码下载】
Dec 06 #jQuery
jQuery动态操作表单示例【基于table表格】
Dec 06 #jQuery
js防抖和节流的深入讲解
Dec 06 #Javascript
angular中两种表单的区别(响应式和模板驱动表单)
Dec 06 #Javascript
You might like
PHP 判断常量,变量和函数是否存在
2009/04/26 PHP
php 文章采集正则代码
2009/12/28 PHP
php从数组中随机抽取一些元素的代码
2012/11/05 PHP
php实现格式化多行文本为Js可用格式
2015/04/15 PHP
thinkphp3.x自定义Action、Model及View的简单实现方法
2016/05/19 PHP
php获取访问者浏览页面的浏览器类型
2017/01/23 PHP
PHP中Cookie的使用详解(简单易懂)
2017/04/28 PHP
PHP使用HTML5 FormData对象提交表单操作示例
2019/07/02 PHP
Jquery获取复选框被选中值的简单方法
2013/07/04 Javascript
javascript求日期差的方法
2016/03/02 Javascript
javascript数组遍历的方法实例分析
2016/09/13 Javascript
js仿iphone秒表功能 计算平均数
2017/01/11 Javascript
折叠菜单及选择器的运用
2017/02/03 Javascript
AngularJS constant和value区别详解
2017/02/28 Javascript
微信小程序实现弹出层效果
2020/05/26 Javascript
vue两组件间值传递 $router.push实现方法
2019/05/15 Javascript
如何使用VSCode愉快的写Python于调试配置步骤
2018/04/06 Python
Python 获取中文字拼音首个字母的方法
2018/11/28 Python
python使用pygame模块实现坦克大战游戏
2020/03/25 Python
解决安装python3.7.4报错Can''t connect to HTTPS URL because the SSL module is not available
2019/07/31 Python
python实现单链表的方法示例
2019/09/03 Python
python通过对字典的排序,对json字段进行排序的实例
2020/02/27 Python
Python读写操作csv和excle文件代码实例
2020/03/16 Python
如何搭建pytorch环境的方法步骤
2020/05/06 Python
Java ExcutorService优雅关闭方式解析
2020/05/30 Python
浅谈anaconda python 版本对应关系
2020/10/07 Python
HTML5 自动聚焦(autofocus)属性使用介绍
2013/08/07 HTML / CSS
美国知名的家庭连锁百货商店:Boscov’s
2017/07/27 全球购物
全球性的在线鞋类品牌:Public Desire
2019/04/03 全球购物
衰败城市英国官网:Urban Decay英国
2020/04/29 全球购物
2014年医院后勤工作总结
2014/12/06 职场文书
2015年幼儿园中班下学期工作总结
2015/05/22 职场文书
Spring Boot两种全局配置和两种注解的操作方法
2021/06/29 Java/Android
实操Python爬取觅知网素材图片示例
2021/11/27 Python
Python实现视频中添加音频工具详解
2021/12/06 Python
微软Win11 全新照片应用面向 Dev预览版推出 新版本上手体验图集
2022/09/23 数码科技