使用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 组合form元素为json格式,asp.net反序列化
Jul 09 Javascript
JavaScript中json对象和string对象之间相互转化
Dec 26 Javascript
jQuery制作仿腾讯web qq用户体验桌面
Aug 20 Javascript
使用jQuery解决IE与FireFox下createElement方法的差异
Nov 14 Javascript
jQuery实现的一个自定义Placeholder属性插件
Aug 11 Javascript
JS中用try catch对代码运行的性能影响分析
Dec 26 Javascript
微信小程序页面间通信的5种方式
Mar 31 Javascript
Angular2中如何使用ngx-translate进行国际化
May 21 Javascript
element-ui 设置菜单栏展开的方法
Aug 22 Javascript
vue项目中实现的微信分享功能示例
Jan 21 Javascript
详解微信小程序中var、let、const用法与区别
Jan 11 Javascript
原生js 实现表单验证功能
Feb 08 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
信用卡效验程序
2006/10/09 PHP
php桌面中心(二) 数据库写入
2007/03/11 PHP
PHP执行linux系统命令的常用函数使用说明
2010/04/27 PHP
linux下为php添加curl扩展的方法
2011/07/29 PHP
详解PHP错误日志的获取方法
2015/07/20 PHP
php代码检查代理ip的有效性
2016/08/19 PHP
PHP实现根据密码长度显示安全条
2017/07/04 PHP
基于jquery的仿百度的鼠标移入图片抖动效果
2010/09/17 Javascript
jQuery Form 页面表单提交的小例子
2013/11/15 Javascript
js 绑定键盘鼠标事件示例代码
2014/02/12 Javascript
javascript实现节点(div)名称编辑
2014/12/17 Javascript
knockoutjs模板实现树形结构列表
2017/07/31 Javascript
Vue实现web分页组件详解
2017/11/28 Javascript
react中使用swiper的具体方法
2018/05/15 Javascript
如何解决vue2.0下IE浏览器白屏问题
2018/09/13 Javascript
vue+高德地图写地图选址组件的方法
2019/05/18 Javascript
微信小程序动态显示项目倒计时
2019/06/20 Javascript
Vue CLI4 Vue.config.js标准配置(最全注释)
2020/06/05 Javascript
html-webpack-plugin修改页面的title的方法
2020/06/18 Javascript
python使用点操作符访问字典(dict)数据的方法
2015/03/16 Python
Python3使用PyQt5制作简单的画板/手写板实例
2017/10/19 Python
Python实现iOS自动化打包详解步骤
2018/10/03 Python
利用Python如何实现一个小说网站雏形
2018/11/23 Python
从pandas一个单元格的字符串中提取字符串方式
2019/12/17 Python
python编程进阶之类和对象用法实例分析
2020/02/21 Python
快速解决Django关闭Debug模式无法加载media图片与static静态文件
2020/04/07 Python
Python-jenkins 获取job构建信息方式
2020/05/12 Python
python实现猜数游戏(保存游戏记录)
2020/06/22 Python
材料加工硕士生求职信
2013/10/10 职场文书
2014元旦晚会策划方案
2014/02/19 职场文书
餐厅采购员岗位职责
2014/03/06 职场文书
2014年社区庆元旦活动方案
2014/03/08 职场文书
2014年廉洁自律承诺书
2014/05/26 职场文书
片区教研活动总结
2014/07/02 职场文书
公司2014年度工作总结
2014/12/10 职场文书
心理学培训心得体会
2016/01/22 职场文书