使用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 相关文章推荐
jQuery插件Timelinr 实现时间轴特效
Oct 04 Javascript
Javascript之Date对象详解
Jun 07 Javascript
jQuery EasyUI菜单与按钮详解
Jul 13 Javascript
JavaScript实现图片轮播组件代码示例
Nov 22 Javascript
js通过classname来获取元素的方法
Nov 24 Javascript
Vue 中批量下载文件并打包的示例代码
Nov 20 Javascript
JavaScript中的高级函数
Jan 04 Javascript
详解如何在React组件“外”使用父组件的Props
Jan 12 Javascript
Element-ui table中过滤条件变更表格内容的方法
Mar 02 Javascript
微信小程序中时间戳和日期的相互转换问题
Jul 09 Javascript
浅谈关于vue中scss公用的解决方案
Dec 02 Javascript
vue+elementui通用弹窗的实现(新增+编辑)
Jan 07 Vue.js
微信小程序冒泡事件及其阻止方法实例分析
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
苏联队长,苏联超人蝙蝠侠,这些登场的“山寨”英雄真的很严肃
2020/04/09 欧美动漫
ftp类(example.php)
2006/10/09 PHP
dede全站URL静态化改造[070414更正]
2007/04/17 PHP
php 无限级 SelectTree 类
2009/05/19 PHP
PHP无限分类代码,支持数组格式化、直接输出菜单两种方式
2011/05/18 PHP
Thinkphp搜索时首页分页和搜索页保持条件分页的方法
2014/12/05 PHP
PHP+iframe模拟Ajax上传文件功能示例
2019/07/02 PHP
jquery无缝向上滚动实现代码
2013/03/29 Javascript
javaScript数组迭代方法详解
2016/04/14 Javascript
概述如何实现一个简单的浏览器端js模块加载器
2016/12/07 Javascript
jQuery使用siblings获取某元素所有同辈(兄弟姐妹)元素用法示例
2017/01/30 Javascript
Vue2.0权限树组件实现代码
2017/08/29 Javascript
Angular2里获取(input file)上传文件的内容的方法
2017/09/05 Javascript
JS将网址url转化为JSON格式的方法
2018/07/02 Javascript
vue-cli设置css不生效的解决方法
2020/02/07 Javascript
使用Vant完成DatetimePicker 日期的选择器操作
2020/11/12 Javascript
[37:23]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#2Secret VS EG第二局
2016/03/04 DOTA
[01:04:09]DOTA2-DPC中国联赛 正赛 iG vs VG BO3 第二场 2月2日
2021/03/11 DOTA
Python转换HTML到Text纯文本的方法
2015/01/15 Python
Python环境变量设置方法
2016/08/28 Python
Python考拉兹猜想输出序列代码实践
2019/07/05 Python
Python使用graphviz画流程图过程解析
2020/03/31 Python
Python装饰器结合递归原理解析
2020/07/02 Python
Python如何急速下载第三方库详解
2020/11/02 Python
详解解决jupyter不能使用pytorch的问题
2021/02/18 Python
Lookfantastic美国/加拿大:英国知名美妆购物网站
2019/03/27 全球购物
Onzie官网:美国时尚瑜伽品牌
2019/08/21 全球购物
成都思必达公司C#程序员招聘面试题
2013/06/26 面试题
收银员的岗位职责范本
2014/02/04 职场文书
毕业生工作求职信
2014/06/30 职场文书
开展党的群众路线教育实践活动工作总结
2014/11/05 职场文书
2014年财政工作总结
2014/12/10 职场文书
2015年底工作总结范文
2015/05/15 职场文书
乡镇党建工作总结2015
2015/05/19 职场文书
商业计划书如何写?关键问题有哪些?
2019/07/11 职场文书
JavaScript 原型与原型链详情
2021/11/02 Javascript