使用react render props實(shí)現(xiàn)倒計(jì)時(shí)的示例代碼
react的組件模式可以觀看Michael Chan的演講視頻,平時(shí)大家常聽(tīng)到的react模式也是HOC, HOC的使用場(chǎng)景很多,譬如react-redux的connect,這里不贅述HOC相關(guān),感興趣可以自行了解。
首先是這樣一個(gè)場(chǎng)景,我的業(yè)務(wù)需要實(shí)現(xiàn)倒計(jì)時(shí),倒計(jì)時(shí)你懂得,倒計(jì)時(shí)經(jīng)常應(yīng)用在預(yù)告一個(gè)活動(dòng)的開(kāi)始,像秒殺,像開(kāi)售搶購(gòu)等,或者活動(dòng)的截止。
我們來(lái)梳理一下這個(gè)倒計(jì)時(shí)的功能:
- 定時(shí)更新時(shí)間,以秒為度;
- 可以更新倒計(jì)時(shí)的截止時(shí)間,比如從10月1日更新為10月2日;
- 倒計(jì)時(shí)結(jié)束,執(zhí)行對(duì)應(yīng)結(jié)束邏輯;
- 倒計(jì)時(shí)結(jié)束,開(kāi)啟另一個(gè)活動(dòng)倒計(jì)時(shí);
- 同時(shí)有多個(gè)倒計(jì)時(shí);
這個(gè)時(shí)候我便開(kāi)始編碼,考慮代碼復(fù)用,我用Class的模式實(shí)現(xiàn)一個(gè)倒計(jì)時(shí):
class Timer { constructor(time, countCb, timeoutCb) { this.countCb = countCb; this.timeoutCb = timeoutCb; this.setDelayTime(time); } intervalId = null; clearInterval = () => { if (this.intervalId) { clearInterval(this.intervalId); } } // 更新倒計(jì)時(shí)的截止時(shí)間 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); // 執(zhí)行回調(diào),由調(diào)用方?jīng)Q定顯示格式 if (typeof this.countCb === 'function') { this.countCb({ day, hour, minute, second, }); } } } export default Timer;
通過(guò)class的方式可以實(shí)現(xiàn)我的上述功能,將格式顯示交給調(diào)用方?jīng)Q定,Timer只實(shí)現(xiàn)倒計(jì)時(shí)功能,這并沒(méi)有什么問(wèn)題,我們看調(diào)用方如何使用:
// 這是一個(gè)react組件部分代碼 componentDidMount() { // 開(kāi)啟倒計(jì)時(shí) this.countDownLiveDelay(); } componentDidUpdate() { // 開(kāi)啟倒計(jì)時(shí) 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 一個(gè)timer對(duì)象 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> ); }
查看這種方式的調(diào)用的缺點(diǎn):調(diào)用方都需要手動(dòng)開(kāi)啟倒計(jì)時(shí),countDownLiveDelay方法調(diào)用
總感覺(jué)不夠優(yōu)雅,直到我看到了react的render props, 突然靈關(guān)一現(xiàn),來(lái)了下面這段代碼:
let delayTime; // 倒計(jì)時(shí)組件 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; } } // 開(kāi)啟計(jì)時(shí) startCountDown() { if (delayTime && !this.timer) { this.timer = setInterval(() => { this.doCount(); }, 1000); } } doCount() { const { onTimeout, } = this.props; // 使用Math.floor((delayTime - Date.now()) / 1000)的話會(huì)導(dǎo)致這里值為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代碼可戳這里
調(diào)用方:
import TimeCountDown from 'TimeCountDown'; function formateTimeStr(num) { return num < 10 ? `0${num}` : num; } // 業(yè)務(wù)調(diào)用倒計(jì)時(shí)組件 class CallTimer extends Component { onTimeout = () => { this.forceUpdate(); } render() { // 傳遞render函數(shù) 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> ) } }
對(duì)比這種方式,通過(guò)傳遞一個(gè)函數(shù)render方法給到TimeCountDown組件,TimeCountDown組件渲染時(shí)執(zhí)行props的render方法,并傳遞TimeCountDown的state進(jìn)行渲染,這就是render props的模式了,這種方式靈活、優(yōu)雅很多,很多場(chǎng)景都可以使用這種方式,而無(wú)需使用HOC。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
React?Suspense解決競(jìng)態(tài)條件詳解
這篇文章主要為大家介紹了React?Suspense解決競(jìng)態(tài)條件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11React中使用setInterval函數(shù)的實(shí)例
這篇文章主要介紹了React中使用setInterval函數(shù)的實(shí)例,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04TypeScript在React項(xiàng)目中的使用實(shí)踐總結(jié)
這篇文章主要介紹了TypeScript在React項(xiàng)目中的使用總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04使用react+redux實(shí)現(xiàn)彈出框案例
這篇文章主要為大家詳細(xì)介紹了使用react+redux實(shí)現(xiàn)彈出框案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08React?Hooks的useState、useRef使用小結(jié)
React Hooks 是 React 16.8 版本引入的新特性,useState和useRef是兩個(gè)常用的Hooks,本文主要介紹了React?Hooks的useState、useRef使用,感興趣的可以了解一下2024-01-01React路由動(dòng)畫(huà)切換實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了react-router 路由切換動(dòng)畫(huà)的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2022-12-12React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式詳解
這篇文章主要介紹了React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式,手動(dòng)路由的跳轉(zhuǎn),主要是通過(guò)Link或者NavLink進(jìn)行跳轉(zhuǎn)的,實(shí)際上我們也可以通JavaScript代碼進(jìn)行跳轉(zhuǎn),需要的朋友可以參考下2022-11-11