前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用react render props实现倒计时

使用react render props实现倒计时

作者头像
IMWeb前端团队
发布2019-12-03 18:16:59
1.2K0
发布2019-12-03 18:16:59
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

本文作者:IMWeb EnjoyChan 原文出处:IMWeb社区 未经同意,禁止转载

使用react render props实现倒计时

react的组件模式可以观看Michael Chan的演讲视频,平时大家常听到的react模式也是HOC, HOC的使用场景很多,譬如react-redux的connect,这里不赘述HOC相关,感兴趣可以自行了解。

首先是这样一个场景,我的业务需要实现倒计时,倒计时你懂得,倒计时经常应用在预告一个活动的开始,像秒杀,像开售抢购等,或者活动的截止。

我们来梳理一下这个倒计时的功能:

  1. 定时更新时间,以秒为度;
  2. 可以更新倒计时的截止时间,比如从10月1日更新为10月2日;
  3. 倒计时结束,执行对应结束逻辑;
  4. 倒计时结束,开启另一个活动倒计时;
  5. 同时有多个倒计时;

这个时候我便开始编码,考虑代码复用,我用Class的模式实现一个倒计时:

代码语言:javascript
复制
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只实现倒计时功能,这并没有什么问题,我们看调用方如何使用:

代码语言:javascript
复制
 // 这是一个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, 突然灵关一现,来了下面这段代码:

代码语言:javascript
复制
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代码可戳这里

调用方:

代码语言:javascript
复制
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。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档