介绍
在本文中,我们将探讨如何使用 React 和 date-fns 库创建倒计时应用程序。本分步教程将指导您完成构建用户友好的倒计时计时器的过程,该计时器允许用户设置结束日期并观看计时器倒计时至零。
我们将讨论 date-fns 库的基本功能、使用它进行日期操作的好处,以及如何将它整合到您的 React 应用程序中。在本教程结束时,您将拥有一个功能强大的倒计时应用程序,它展示了 date-fns 库的强大功能和便利性。
date-fns 库
那么,date-fns 库是什么?date-fns 库是一组函数,可以轻松地在 React 应用程序中处理日期和时间。它可以帮助您以用户友好的方式格式化、解析、比较和操作日期。
在编写与日期相关的函数时,JavaScript 因其糟糕的日期支持而臭名昭著。date-fns 库减轻了管理日期的负担,使您可以专注于项目的其他方面。
这个库的另一个好处是它使您能够只导入项目所需的功能,从而使应用程序更小,加载速度更快。与其他库不同,无需导入所有内容,使 date-fns 成为处理日期的快速便捷解决方案!
选定的库函数
对于倒计时时钟应用程序,我们将使用 date-fns 库中的以下八个函数:
- format: 根据给定的格式字符串格式化日期。
- formatDuration: 将持续时间对象转换为人类可读的字符串。
- intervalToDuration: 计算两个日期之间的持续时间作为持续时间对象。
- isBefore: 判断第一个日期是否早于第二个日期。
- addDays: 将指定天数添加到给定日期。
- 添加: 将指定的时间量添加到给定日期。
- endOfDay: 返回给定日期的结束日期。
- differenceInMilliseconds: 计算两个日期之间的毫秒差。
**注意: ** 由于我们的重点是学习 date-fns 库,因此我们将保持这个项目对初学者友好,而不创建单独的组件。
入口
在 JSX 文件的顶部,我们将导入必要的钩子、函数和组件:
- 使用状态
- 使用效果
- 日期-fns
- 反应五彩纸屑
- 应用程序.css
import { useState, useEffect } from 'react';
import { format, formatDuration, intervalToDuration, isBefore, addDays, add, endOfDay, differenceInMilliseconds } from 'date-fns';
import Confetti from 'react-confetti';
import './App.css'
应用功能
我们将在这个 App 函数中编写我们的代码:
function App() {
});
在 App 函数中,我们最初设置了状态变量。
变量初始化:
- now: 当前日期和时间。
- endOfToday: 当天结束。
- initialEndDate: 从 localStorage 中检索或设置为 NULL。
状态变量初始化:
- countdown: 表示倒数计时器的字符串。
- countdownEnded: 一个布尔值,指示倒计时是否已结束。
- endDate: 倒计时的结束日期,从 localStorage 检索或设置为从当前日期算起的一天。
const now = new Date();
const endOfToday = endOfDay(now);
const initialEndDate = localStorage.getItem("endDate");
const [countdown, setCountdown] = useState('');
const [countdownEnded, setCountdownEnded] = useState(false);
const [endDate, setEndDate] = useState(() => {
return initialEndDate ? new Date(initialEndDate) : new Date(Date.now() + differenceInMilliseconds(endOfToday, now));
});
为了让这个应用程序按预期运行,我们需要使用本地存储。对于应用程序的首次使用,当前日期加一天被设置为默认值。当用户使用日期选择器选择一个日期时,该日期将成为新的结束日期值,并存储在本地存储中。这样,当用户在关闭应用程序后返回应用程序时,倒计时会反映他们选择的结束日期。
处理日期变化的函数
定义 handleDateChange 函数:
- 当用户更改日期输入时调用它。
- 它以东部标准时间 (EST) 计算结束日期并将其保存到 localStorage。
- 它更新 endDate 状态并将 countdownEnded 状态重置为 false。
function handleDateChange(event) {
const chosenDate = new Date(event.target.value);
const dateEST = add(chosenDate, { hours: 4 });
localStorage.setItem("endDate", dateEST);
setEndDate(dateEST);
setCountdownEnded(false);
};
当用户选择新日期时,将触发handleDateChange 。 他们选择的值存储在chosenDate变量中。
我在使用这个库时遇到了一个问题。出于某种原因,时间以四个小时的时差存储。为简单起见,我创建了一个名为 **dateEST 的新变量, ** 并通过添加缺少的四个小时时区差异来使用它。(我确实找到了其他 React 库来处理时区,但选择增加时间)。
用户选择的日期保存在本地存储和endDate变量中。setCountdownEnded布尔值更改为 false。
倒计时
使用 useEffect 挂钩为倒计时逻辑设置间隔:
- 它每 1000 毫秒(1 秒)运行一次并计算剩余持续时间。
- 如果结束日期已过去,它将countdownEnded设置为 true 并清除间隔。
- 否则,它会用剩余时间更新倒计时状态。
useEffect(() => {
const interval = setInterval(() => {
const now = new Date();
const duration = intervalToDuration({ start: now, end: endDate });
if (isBefore(endDate, now)) {
setCountdownEnded(true);
clearInterval(interval);
} else {
setCountdown(`${formatDuration(duration)}`);
}
}, 1000);
return () => clearInterval(interval);
}, [endDate]);
这里是所有魔法发生的地方!在 JavaScript setInterval函数中,您将看到使用 date-fns 库提供的函数轻松处理日期。
我们创建一个名为now的新变量,并使用 date-fns 函数intervalToDuration将其与 endDate变量进行比较。当倒计时处于活动状态时,每秒都会使用 date-fns formatDuration函数保存和呈现剩余时间。
在 if 语句中,我们使用 date-fns 函数isBefore来检查endDate变量的值何时早于当前日期。如果是,则倒计时结束!然后我们将setCountdownEnded布尔变量设置为 true 并停止倒计时间隔。
渲染组件
return 语句呈现组件:
- 如果倒计时结束,则显示五彩纸屑组件。
- 显示图像和倒数计时器 UI。
- 日期输入的最小值设置为今天的日期。
- 显示格式化的结束日期和倒计时计时器或“倒计时结束!” 信息。
return (
<div className='countdown-timer-container'>
{countdownEnded && <Confetti />}
<img src='https://m.media-amazon.com/images/M/MV5BYzA3ZjQ4YTItNjRhYS00YzRkLTg4NTEtMTYyOGZjNDFmYWM5XkEyXkFqcGdeQXVyMTkxNjUyNQ@@._V1_.jpg' width={`250`} alt="Countdown clock illustration" />
<div className='timer'>
<h2>Countdown Clock</h2>
<input type="date" min={format(new Date(), "yyyy-MM-dd")} onChange={handleDateChange} />
<h3>{format(endDate, "MMMM do, yyyy")}</h3>
{countdownEnded && <h4>Countdown Ended!</h4>}
{!countdownEnded && <h4>{countdown}</h4>}
</div>
</div>
);
React-confetti是一个有趣的 React 库,用于在您的应用程序中轻松创建五彩纸屑动画。除了在 JSX 文件顶部的初始导入之外没有任何其他步骤,您需要做的就是添加<Confetti / >
React 元素。在我们的例子中,我们只想在倒计时结束时渲染它,我们通过检查 countdownEnded变量是否为真来完成。如果是,则显示 Confetti 元素。如果没有,则不显示任何内容。
对于这个专注于学习 date-fns 库的简单应用程序,我决定使用具有在线源属性的图像标签。
对于用户,我在 React 组件中添加了一个带有日期类型的输入元素和一个用于处理日期更改的事件处理程序。我使用min属性将最小允许值设置为当前日期。
如您所见,我们可以使用format date-fns 函数轻松格式化日期。
类似于五彩纸屑 React 元素,我们只在倒计时完成时渲染适当的元素。
**注意: ** 要呈现预期日期,如果 initialEndDate 变量为 NULL,我们将 endDate 添加 1 天。
这是完整的 JSX 文件
import { useState, useEffect } from 'react';
import { format, formatDuration, intervalToDuration, isBefore, addDays, add, endOfDay, differenceInMilliseconds } from 'date-fns';
import Confetti from 'react-confetti';
import './App.css'
function App() {
const now = new Date();
const endOfToday = endOfDay(now);
const initialEndDate = localStorage.getItem("endDate");
const [countdown, setCountdown] = useState('');
const [countdownEnded, setCountdownEnded] = useState(false);
const [endDate, setEndDate] = useState(() => {
return initialEndDate ? new Date(initialEndDate) : new Date(Date.now() + differenceInMilliseconds(endOfToday, now));
});
function handleDateChange(event) {
const chosenDate = new Date(event.target.value);
const dateEST = add(chosenDate, { hours: 4 });
localStorage.setItem("endDate", dateEST);
setEndDate(dateEST);
setCountdownEnded(false);
};
// useEffect hook: Updates the countdown every second and checks if the countdown has ended. If it has, it clears the interval and sets the countdownEnded state to true.
useEffect(() => {
const interval = setInterval(() => {
const now = new Date();
const duration = intervalToDuration({ start: now, end: endDate });
if (isBefore(endDate, now)) {
setCountdownEnded(true);
clearInterval(interval);
} else {
setCountdown(`${formatDuration(duration)}`);
}
}, 1000);
return () => clearInterval(interval);
}, [endDate]);
return (
<div className='countdown-timer-container'>
{countdownEnded && <Confetti />}
<img src='https://m.media-amazon.com/images/M/MV5BYzA3ZjQ4YTItNjRhYS00YzRkLTg4NTEtMTYyOGZjNDFmYWM5XkEyXkFqcGdeQXVyMTkxNjUyNQ@@._V1_.jpg' width={`250`} alt="Countdown clock illustration" />
<div className='timer'>
<h2>Countdown Clock</h2>
<input type="date" min={format(new Date(), "yyyy-MM-dd")} onChange={handleDateChange} />
<h3>{format(endDate, "MMMM do, yyyy")}</h3>
{countdownEnded && <h4>Countdown Ended!</h4>}
{!countdownEnded && <h4>{countdown}</h4>}
</div>
</div>
);
}
export default App;