使用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 相关文章推荐
javascript while语句和do while语句的区别分析
Dec 08 Javascript
JS编程小常识很有用
Nov 26 Javascript
用js实现trim()的解决办法
Apr 16 Javascript
详解jQuery插件开发中的extend方法
Nov 19 Javascript
jQuery 删除或是清空某个HTML元素示例
Aug 04 Javascript
jQuery仿gmail实现fixed布局的方法
May 27 Javascript
jQuery 1.9.1源码分析系列(十三)之位置大小操作
Dec 02 Javascript
深入浅析search 搜索框的写法
Aug 02 Javascript
jQuery EasyUI的TreeGrid查询功能实现方法
Aug 08 jQuery
JS实现点击按钮随机生成可拖动的不同颜色块示例
Jan 30 Javascript
jQuery实现的分页插件完整示例
May 26 jQuery
创建与框架无关的JavaScript插件
Dec 01 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
德劲1102收音机的打理维修案例
2021/03/02 无线电
PHP自带ZIP压缩、解压缩类ZipArchiv使用指南
2015/03/03 PHP
Yii框架参数化查询中IN查询只能查询一个的解决方法
2017/05/20 PHP
JQuery中getJSON的使用方法
2010/12/13 Javascript
UpdatePanel和Jquery冲突的解决方法
2013/04/01 Javascript
js 针对html DOM元素操作等经验累积
2014/03/11 Javascript
JQuery对表单元素的基本操作使用总结
2014/07/18 Javascript
jQuery实现返回顶部功能适合不支持js的浏览器
2014/08/19 Javascript
js运动动画的八个知识点
2015/03/12 Javascript
javascript获取文档坐标和视口坐标
2015/05/26 Javascript
深入理解JS中的substr和substring
2016/04/26 Javascript
JS简单实现点击复制链接的方法
2016/08/03 Javascript
jQuery事件与动画基础详解
2017/02/23 Javascript
JS仿JQuery选择器功能
2017/03/08 Javascript
在Angular中使用JWT认证方法示例
2018/09/10 Javascript
vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
2018/11/29 Javascript
说说如何使用Vuex进行状态管理(小结)
2019/04/14 Javascript
python操作列表的函数使用代码详解
2017/12/28 Python
python多进程中的内存复制(实例讲解)
2018/01/05 Python
python定向爬取淘宝商品价格
2018/02/27 Python
Python3爬虫之urllib携带cookie爬取网页的方法
2018/12/28 Python
python使用requests.session模拟登录
2019/08/09 Python
Python中注释(多行注释和单行注释)的用法实例
2019/08/28 Python
Django添加bootstrap框架时无法加载静态文件的解决方式
2020/03/27 Python
意大利奢侈品综合电商网站:MODES
2019/12/14 全球购物
Skyscanner香港:机票比价, 平机票和廉价航空机票预订
2020/02/07 全球购物
餐厅楼面部长岗位职责范文
2014/02/16 职场文书
机关副主任个人四风问题整改措施
2014/09/26 职场文书
授权委托书
2015/01/28 职场文书
风之谷观后感
2015/06/11 职场文书
保护环境的宣传语
2015/07/13 职场文书
运动会开幕式新闻稿
2015/07/17 职场文书
2019感恩宣传标语!
2019/07/05 职场文书
创业计划书之家政服务
2019/09/18 职场文书
nginx反向代理时如何保持长连接
2021/03/31 Servers