使用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 相关文章推荐
ext 列表页面关于多行查询的办法
Mar 25 Javascript
JS实现在Repeater控件中创建可隐藏区域的代码
Sep 16 Javascript
QUnit jQuery的TDD框架
Nov 04 Javascript
jquery实现手风琴效果
Nov 20 Javascript
jQuery mobile 移动web(6)
Dec 20 Javascript
jQuery下拉框的简单应用
Jun 24 Javascript
微信小程序实战之顶部导航栏(选项卡)(1)
Jun 19 Javascript
vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
Apr 13 Javascript
javascript中一些奇葩的日期换算方法总结
Nov 14 Javascript
Node.js中Koa2在控制台输出请求日志的方法示例
May 02 Javascript
Django模板继承 extend标签实例代码详解
May 16 Javascript
几款主流好用的富文本编辑器(所见即所得常用编辑器)介绍
Mar 17 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/06/11 PHP
PHP获取163、gmail、126等邮箱联系人地址【已测试2009.10.10】
2009/10/11 PHP
常见的PHP五种设计模式小结
2011/03/23 PHP
php过滤敏感词的示例
2014/03/31 PHP
php约瑟夫问题解决关于处死犯人的算法
2015/03/23 PHP
php格式化json函数示例代码
2016/05/12 PHP
PHP单例模式与工厂模式详解
2017/08/29 PHP
Laravel 5使用Laravel Excel实现Excel/CSV文件导入导出的功能详解
2017/10/11 PHP
php使用curl_init()和curl_multi_init()多线程的速度比较详解
2018/08/15 PHP
JavaScript语法着色引擎(demo及打包文件下载)
2007/06/13 Javascript
兼容IE、FireFox、Chrome等浏览器的xml处理函数js代码
2011/11/30 Javascript
JS实现弹出浮动窗口(支持鼠标拖动和关闭)实例详解
2015/08/06 Javascript
angular学习之从零搭建一个angular4.0项目
2017/07/10 Javascript
js禁止浏览器页面后退功能的实例(推荐)
2017/09/01 Javascript
BootStrap自定义popover,点击区域隐藏功能的实现
2018/01/23 Javascript
node.js基础知识小结
2018/02/26 Javascript
解决element-ui中下拉菜单子选项click事件不触发的问题
2018/08/22 Javascript
微信小程序如何修改radio和checkbox的默认样式和图标
2019/07/24 Javascript
微信小程序 点击切换样式scroll-view实现代码实例
2019/10/11 Javascript
vant 解决tab切换插件标题样式自定义的问题
2020/11/13 Javascript
[02:28]DOTA2 2017国际邀请赛小组赛回顾
2017/08/09 DOTA
python导出chrome书签到markdown文件的实例代码
2017/12/27 Python
Django安装配置mysql的方法步骤
2018/10/15 Python
python 删除字符串中连续多个空格并保留一个的方法
2018/12/22 Python
python requests使用socks5的例子
2019/07/25 Python
Python时间序列缺失值的处理方法(日期缺失填充)
2019/08/11 Python
CSS3 倾斜的网页图片库实例教程
2009/11/14 HTML / CSS
俄罗斯购买自行车网站:Vamvelosiped
2021/01/29 全球购物
四年大学生活的自我评价范文
2014/02/07 职场文书
上班迟到检讨书
2014/09/15 职场文书
教师学习三严三实心得体会
2014/10/13 职场文书
创先争优承诺书
2015/01/20 职场文书
销售员自我评价
2015/03/11 职场文书
python 解决微分方程的操作(数值解法)
2021/05/26 Python
用python画城市轮播地图
2021/05/28 Python
使用 MybatisPlus 连接 SqlServer 数据库解决 OFFSET 分页问题
2022/04/22 SQL Server