视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
使用react render props实现倒计时的示例代码
2020-11-27 22:02:57 责编:小采
文档


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

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

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

  • 定时更新时间,以秒为度;
  • 可以更新倒计时的截止时间,比如从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 / 800);
     const hour = Math.floor((timeDiffSecond % 800) / 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 / 800);
     const hour = Math.floor((timeDiffSecond % 800) / 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。

    下载本文
    显示全文
    专题