我发现做一些在Web开发中广泛使用的小组件非常有趣。当我是一名初级Web开发人员时,如果需要构建某些功能部件,我倾向于在线搜索库或插件。实施它可能会很困难,因为我没有尝试去思考它的实际工作原理,而我不得不依靠写得不好的文档。有时,自定义也很困难,因为很难理解为什么作者以自己的方式制作了插件。
日历是最常见的例子之一。在线上有很多插件,但是很少有真正能教您如何工作的插件。当我在上一家公司担任初级开发人员时,我的任务是定制一个集成了一些业务需求的日历,而我在网上找到的所有库都无法满足我的需求。然后,我意识到,为什么不从头开始构建自己的日历?
不难 让我们用React和Styled Component来做!
解
如果您希望在不阅读我的解释的情况下快速实施,那么最终的实现可以在“ 简单反应日历”中找到。
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 ? 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
:a年每个月的天数数组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
为了进一步扩展,您也可以使默认值来自该组件的属性)。
除了当前的日期,月份和年份,您还需要startDay
确定每月的第一天是一周中的哪一天(星期一,星期二或其他)。知道是哪一天后,您可以更轻松地确定日历中所有日期的位置。
创建所有状态后,我们还需要管理它们的更新。我们应date
变量作为切入点的计算day
,month
,year
和startDay
。因此,我们可以使用useEffect
react hook来更新day
,和month
,year
并startDay
具有的依赖关系date
,以便稍后在单击日历中的任何一天时,我们可以调用setDate()
update 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();
}
检查是否在in年
我们还需要检查当前是否处于a年,以便我们可以显示2月的正确天数。
我从Wikipedia中提取了一张图片,以更好地说明determination年的确定。
很显然,如果一年是a年,则该年可以被4除以400,但不能被100除。
例如,
- 2020年是a年,因为它可以被4整除
- 2010年不是a年,因为它不能被4整除
- 2000年是a年,因为它可以被400整除
- 1900年不是a年。虽然1900可被4整除,但也可被100整除
(最好为此编写一个单元测试!)
function isLeapYear(year: number) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
用TSX建立日历!
最后,我们可以通过添加渲染部分来完成该组件。
setDate()
单击标题时将触发标题中的2个按钮。它将然后触发useEffect()
回调,然后更新day
,month
,year
和startDay
,其中month
并day
显示在标题的标题,day
用来确定一天为当前选择的日期,开始一天来计算,应该有多少空块前的有每月的第一天。
const days = isLeapYear ? 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>
);
结论
今天,我分享了如何使用带样式的组件创建一个简单的反应压延机。这并不像想象的那么难,因为我认为唯一关键的部分是知道您需要确定一周的第一天是星期几。如果可以,则可以确定当天的所有职位。其余的工作只是修饰您的组件,使其更具吸引力。
谢谢您的阅读!!