使用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 相关文章推荐
Extjs407 getValue()和getRawValue()区别介绍
May 21 Javascript
setInterval()和setTimeout()的用法和区别示例介绍
Nov 17 Javascript
使用javascript实现Iframe自适应高度
Dec 24 Javascript
JavaScript实现多个重叠层点击切换效果的方法
Apr 24 Javascript
jQuery实现延迟跳转的方法
Jun 05 Javascript
Angular页面间切换及传值的4种方法
Nov 04 Javascript
bootstrap table实例详解
Jan 06 Javascript
详解用node-images 打造简易图片服务器
May 08 Javascript
Nuxt.js实现校验访问浏览器类型的中间件
Aug 24 Javascript
详解vue 数组和对象渲染问题
Sep 21 Javascript
ES6入门教程之Array.from()方法
Mar 23 Javascript
vue响应式系统之observe、watcher、dep的源码解析
Apr 09 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 ss7.5的数据调用 (笔记)
2010/03/08 PHP
php数组函数序列之array_intersect() 返回两个或多个数组的交集数组
2011/11/10 PHP
php获取给定日期相差天数的方法分析
2017/02/20 PHP
jQuery EasyUI API 中文文档 搜索框
2011/09/29 Javascript
JS的Document属性和方法小结
2013/09/17 Javascript
探讨JQUERY JSON的反序列化类 using问题的解决方法
2013/12/19 Javascript
jquery库文件略庞大用纯js替换jquery的方法
2014/08/12 Javascript
详解Bootstrap glyphicons字体图标
2016/01/04 Javascript
js在ie下打开对话窗口的方法小结
2016/10/24 Javascript
Vue.js常用指令之循环使用v-for指令教程
2017/06/27 Javascript
vue.js如何更改默认端口号8080为指定端口的方法
2017/07/14 Javascript
VUE安装使用教程详解
2019/06/03 Javascript
JavaScript写个贪吃蛇小游戏(超详细)
2020/03/17 Javascript
vuecli项目构建SSR服务端渲染的实现
2020/10/30 Javascript
[01:15:29]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#2Secret VS EG第三局
2016/03/04 DOTA
[02:08]什么藏在DOTA2 TI9“小紫本”里?斧王历险记告诉你!
2019/05/17 DOTA
Python爬虫的两套解析方法和四种爬虫实现过程
2018/07/20 Python
python3 小数位的四舍五入(用两种方法解决round 遇5不进)
2019/04/11 Python
python统计文章中单词出现次数实例
2020/02/27 Python
Python递归实现打印多重列表代码
2020/02/27 Python
css3.0 图形构成实例练习二
2013/03/19 HTML / CSS
印度尼西亚在线时尚购物网站:ZALORA印尼
2016/08/02 全球购物
Sunglasses Shop荷兰站:英国最大的太阳镜独立在线零售商和供应商
2017/01/08 全球购物
20世纪40年代连衣裙和复古服装:The Seamstress Of Bloomsbury
2018/07/24 全球购物
Linux机考试题
2015/07/17 面试题
请用Python写一个获取用户输入数字,并根据数字大小输出不同信息的脚本
2014/05/20 面试题
入党自我鉴定范文
2013/10/04 职场文书
茶叶生产计划书
2014/01/10 职场文书
卫校中专生的自我评价
2014/01/15 职场文书
安全生产管理合理化建议书
2014/03/12 职场文书
公共艺术专业自荐信
2014/09/01 职场文书
欢迎家长标语
2014/10/08 职场文书
实习证明模板
2015/06/16 职场文书
2015国庆节66周年标语
2015/07/30 职场文书
2016元旦文艺汇演主持词(开场白+结束语)
2015/12/03 职场文书
浅谈pytorch中stack和cat的及to_tensor的坑
2021/05/20 Python