使用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.js實(shí)現(xiàn)頁(yè)面登錄跳轉(zhuǎn)示例
本文主要介紹了react.js實(shí)現(xiàn)頁(yè)面登錄跳轉(zhuǎn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
如何使用React的VideoPlayer構(gòu)建視頻播放器
本文介紹了如何使用React構(gòu)建一個(gè)基礎(chǔ)的視頻播放器組件,并探討了常見(jiàn)問(wèn)題和易錯(cuò)點(diǎn),通過(guò)組件化思想和合理管理狀態(tài),可以實(shí)現(xiàn)功能豐富且性能優(yōu)化的視頻播放器2025-01-01
nodejs和react實(shí)現(xiàn)即時(shí)通訊簡(jiǎn)易聊天室功能
這篇文章主要介紹了nodejs和react實(shí)現(xiàn)即時(shí)通訊簡(jiǎn)易聊天室功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08
React render核心階段深入探究穿插scheduler與reconciler
這篇文章主要介紹了React render核心階段穿插scheduler與reconciler,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-11-11
react搭建在線編輯html的站點(diǎn)通過(guò)引入grapes實(shí)現(xiàn)在線拖拉拽編輯html
Grapes插件是一種用于Web開(kāi)發(fā)的開(kāi)源工具,可以幫助用戶快速創(chuàng)建動(dòng)態(tài)和交互式的網(wǎng)頁(yè)元素,它還支持多語(yǔ)言和多瀏覽器,適合開(kāi)發(fā)響應(yīng)式網(wǎng)頁(yè)和移動(dòng)應(yīng)用程序,這篇文章主要介紹了react搭建在線編輯html的站點(diǎn)通過(guò)引入grapes實(shí)現(xiàn)在線拖拉拽編輯html,需要的朋友可以參考下2023-08-08

