如何使用样式组件创建简单的 React 日历
解决方案
解释
结论
访问我的博客查看原始帖子:如何使用样式组件创建简单的 React 日历
我发现制作在 Web 开发中广泛使用的小组件非常有趣。当我还是一名初级 Web 开发者时,如果需要构建一些功能,我倾向于在线搜索库或插件。实现起来可能很困难,因为我没有尝试去思考它的实际工作原理,而且不得不依赖编写糟糕的文档。有时,定制也很困难,因为很难理解作者为什么要以他们的方式设计插件。
日历是最常见的例子之一。网上有很多插件,但很少有真正教你如何使用的。我在上一家公司做初级开发时,被要求定制一个日历,并集成一些业务需求,但我在网上找到的所有库都无法满足我的需求。后来我意识到,为什么不从头开始构建自己的日历呢?
这并不难。让我们使用React和Styled Component来实现吧!
解决方案
如果您希望快速实现而不阅读我的解释,可以在simple-react-calendar中找到最终实现。
import * as React from 'react';
import { useState, useEffect } from 'react';
import styled, { css } from 'styled-components';
const Frame = styled.div`
width: 400px;
border: 1px solid lightgrey;
box-shadow: 2px 2px 2px #eee;
`;
const Header = styled.div`
font-size: 18px;
font-weight: bold;
padding: 10px 10px 5px 10px;
display: flex;
justify-content: space-between;
background-color: #f5f6fa;
`;
const Button = styled.div`
cursor: pointer;
`;
const Body = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
`;
const Day = styled.div`
width: 14.2%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
${props =>
props.isToday &&
css`
border: 1px solid #eee;
`}
${props =>
props.isSelected &&
css`
background-color: #eee;
`}
`;
export function Calendar() {
const DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DAYS_OF_THE_WEEK = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'];
const MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
const today = new Date();
const [date, setDate] = useState(today);
const [day, setDay] = useState(date.getDate());
const [month, setMonth] = useState(date.getMonth());
const [year, setYear] = useState(date.getFullYear());
const [startDay, setStartDay] = useState(getStartDayOfMonth(date));
useEffect(() => {
setDay(date.getDate());
setMonth(date.getMonth());
setYear(date.getFullYear());
setStartDay(getStartDayOfMonth(date));
}, [date]);
function getStartDayOfMonth(date: Date) {
return new Date(date.getFullYear(), date.getMonth(), 1).getDay();
}
function isLeapYear(year: number) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
const days = isLeapYear(date.getFullYear()) ? DAYS_LEAP : DAYS;
return (
<Frame>
<Header>
<Button onClick={() => setDate(new Date(year, month - 1, day))}>Prev</Button>
<div>
{MONTHS[month]} {year}
</div>
<Button onClick={() => setDate(new Date(year, month + 1, day))}>Next</Button>
</Header>
<Body>
{DAYS_OF_THE_WEEK.map(d => (
<Day key={d}>
<strong>{d}</strong>
</Day>
))}
{Array(days[month] + (startDay - 1))
.fill(null)
.map((_, index) => {
const d = index - (startDay - 2);
return (
<Day
key={index}
isToday={d === today.getDate()}
isSelected={d === day}
onClick={() => setDate(new Date(year, month, d))}
>
{d > 0 ? d : ''}
</Day>
);
})}
</Body>
</Frame>
);
}
解释
初始化日历组件
组件的初始化比较简单,首先导入必要的库,然后创建一个名为 的函数组件Calendar
。
在组件内部,我们div
暂时返回一个空的,并添加一些常量,它们是
DAYS
:正常年份中每个月的天数数组DAYS_LEAP
:闰年每个月的天数数组DAYS_OF_THE_WEEK
:一周中各天的名称数组MONTHS
:月份名称数组
import * as React from 'react';
import { useState, useEffect } from 'react';
import styled, { css } from 'styled-components';
export function Calendar() {
const DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DAYS_OF_THE_WEEK = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'];
const MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
// Will be implemented below
return (
<div />
);
}
识别组件布局
现在让我们来决定日历组件的布局。由于我们构建的是一个基础的日历组件,所以我们只需要一个标题为当前月份和年份的标题,以及一个上个月按钮和一个下个月按钮。
至于正文部分,它由两部分组成,一行表示星期几,多行表示实际日期。
现在,让我们使用样式组件创建这些部分并将它们放在日历功能组件上方。
const Frame = styled.div`
width: 400px;
border: 1px solid lightgrey;
box-shadow: 2px 2px 2px #eee;
`;
const Header = styled.div`
font-size: 18px;
font-weight: bold;
padding: 10px 10px 5px 10px;
display: flex;
justify-content: space-between;
background-color: #f5f6fa;
`;
const Button = styled.div`
cursor: pointer;
`;
const Body = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
`;
const Day = styled.div`
width: 14.2%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
${props =>
props.isToday &&
css`
border: 1px solid #eee;
`}
${props =>
props.isSelected &&
css`
background-color: #eee;
`}
`;
请注意:
- 我使用
14.2%
作为组件的宽度Day
,因为一周/行只能有 7 天,100% / 7
大约为14.2%
。 - 对于
Day
样式组件,我将检查 2 个道具,如果日期是今天,isToday
则显示灰色边框,如果选择了,则显示灰色背景isSelected
使用 React Hooks 管理日期/月份/年份状态
日历必须显示当前日期、月份和年份。它们被视为组件的状态useState
。因此,我们使用React Hook 来管理这些状态。它们的初始值默认从今天的日期生成(您也可以将此默认值设置date
为组件的 prop,以便进一步扩展)。
除了当前日期、月份和年份,你还需要startDay
确定每个月的第一天是星期几(星期一、星期二或其他)。知道了日期后,你就能更轻松地识别日历中所有日期的位置。
创建所有状态后,我们还需要管理它们的更新。我们应该将date
变量作为计算 、 和 的入口点day
。因此,我们可以使用React Hook 来更新、month
和,并依赖,这样以后,当我们点击日历中的任意一天时,我们就可以调用来更新,并触发其余状态也进行更新。year
startDay
useEffect
day
month
year
startDay
date
setDate()
date
const today = new Date();
const [date, setDate] = useState(today);
const [day, setDay] = useState(date.getDate());
const [month, setMonth] = useState(date.getMonth());
const [year, setYear] = useState(date.getFullYear());
const [startDay, setStartDay] = useState(calculateStartDayOfMonth(date));
useEffect(() => {
setDay(date.getDate());
setMonth(date.getMonth());
setYear(date.getFullYear());
setStartDay(calculateStartDayOfMonth(date));
}, [date]);
获取每月的开始日期
如上所述,我们需要获取月份的开始日期,这相当简单直接。
function getStartDayOfMonth(date: Date) {
return new Date(date.getFullYear(), date.getMonth(), 1).getDay();
}
检查是否是闰年
我们还需要检查当前是否处于闰年,以便我们能够显示二月份的正确天数。
我从维基百科中提取了一张图片,以便更好地说明闰年的确定。
很明显,如果某一年是闰年,那么该年份可以被 4 和 400 整除,但不能被 100 整除。
例如,
- 2020 年是闰年,因为它可以被 4 整除
- 2010 年不是闰年,因为它不能被 4 整除
- 2000 年是闰年,因为它能被 400 整除
- 1900 年不是闰年。虽然 1900 可以被 4 整除,但也可以被 100 整除
(最好为其编写一个单元测试!!)
function isLeapYear(year: number) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
使用 TSX 构建日历!
最后,我们可以通过添加渲染部分来完成组件。
标题栏中的两个按钮setDate()
在点击时触发。点击后会触发useEffect()
回调,并更新day
、month
和,其中year
和显示在标题栏的标题中,用于判断该日期是否为当前选定的日期,并计算起始日期在本月 1 日之前应有多少个空块。startDay
month
day
day
const days = isLeapYear(date.getFullYear()) ? DAYS_LEAP : DAYS;
return (
<Frame>
<Header>
<Button onClick={() => setDate(new Date(year, month - 1, day))}>Prev</Button>
<div>
{MONTHS[month]} {year}
</div>
<Button onClick={() => setDate(new Date(year, month + 1, day))}>Next</Button>
</Header>
<Body>
{DAYS_OF_THE_WEEK.map(d => (
<Day key={d}>
<strong>{d}</strong>
</Day>
))}
{Array(days[month] + (startDay - 1))
.fill(null)
.map((_, index) => {
const d = index - (startDay - 2);
return (
<Day
key={index}
isToday={d === today.getDate()}
isSelected={d === day}
onClick={() => setDate(new Date(year, month, d))}
>
{d > 0 ? d : ''}
</Day>
);
})}
</Body>
</Frame>
);
结论
今天我分享了如何用样式化的组件创建一个简单的 React 日历。它并没有想象的那么难,因为我认为唯一关键的部分就是确定一周的第一天是星期几。如果你能做到这一点,你就能确定所有日期的位置。剩下的工作只是修饰你的组件,让它更具吸引力。
谢谢阅读!!
鏂囩珷鏉ユ簮锛�https://dev.to/zhiyueyi/how-to-create-a-simple-react-calendar-with-styled-component-42n6