基于React的前端日历组件编写教程
发布时间:
本文字数:531 字 阅读完需:约 2 分钟
前言
前端日历组件可以在很多组件库中找到,但是如果有一些更加自定义的需求,那么就需要我们从0开始写一个全新的日历组件,这样的话自定义更加方便一些。
思路
首先,我们的日历要借助js的date相关的方法。比如
- 当前月份的第一天:
const firstDayOfMonth = new Date(year, month, 1);
- 当前月份的最后一天:
const lastDayOfMonth = new Date(year, month + 1, 0);
- 上个月份的最后一天:
const lastDayOfLastMonth = new Date(year, month, 0);
- 第一天是星期几:
const firstDayOfWeek = firstDayOfMonth.getDay();
如果日历中有事件,思路是遍历事件数组,将每一个事件item的date的年、月、日与日历的每一天的年月日进行比对,比对后插入日历数组中。
如果是React的话,可以利用useEffect
监听数组的变化,当获取事件数组时或者手动切换月份时,更新日历即可。
代码展示
日历的html代码
<div className={css.calendar}>
<div className={css.calendarDays}>
{datePicker}
{dateData.map((dayUnit, index) => (
<div key={index} className={css.calendarDay}>
<div>
<div className={css.calendarNumber}>
{dayUnit?.day === 1 && `${month + 1}月${dayUnit.day}日`}
{dayUnit?.day !== 1 && dayUnit?.day}
</div>
<div className={css.calendarEvent}>
{dayUnit?.event.map((item) => {
return (
<CalendarEventTag key={item.eventId} event={item} onDeleteClick={() => {}} />
);
})}
</div>
</div>
</div>
))}
</div>
</div>
日历的计算
const [dateData, setDateData] = useState<(dateUnit | null)[]>([]);
// 当前月份的第一天
const firstDayOfMonth = new Date(year, month, 1);
// 当前月份的最后一天
const lastDayOfMonth = new Date(year, month + 1, 0);
// 上个月份的最后一天
const lastDayOfLastMonth = new Date(year, month, 0);
// 第一天是星期几
const firstDayOfWeek = firstDayOfMonth.getDay();
const [haveUpdatedEvent, setHaveUpdatedEvent] = useState(false);
useEffect(() => {
console.log('dateData', dateData);
console.log('month', month);
setHaveUpdatedEvent(false);
// 生成日历中的日期数组,使用 null 表示下一个月的日期
const daysInMonth: (dateUnit | null)[] = [];
const totalDays = lastDayOfMonth.getDate();
for (let i = firstDayOfWeek; i > 0; i--) {
const day = lastDayOfLastMonth.getDate() - i + 1;
daysInMonth.push({
date: new Date(lastDayOfLastMonth.getFullYear(), lastDayOfLastMonth.getMonth(), day),
day: day,
event: [],
});
}
for (let i = 1; i <= totalDays; i++) {
daysInMonth.push({ date: new Date(year, month, i), day: i, event: [] });
}
if (daysInMonth.length % 7 !== 0) {
const addNullNums = 7 - (daysInMonth.length % 7);
for (let i = 0; i < addNullNums; i++) {
daysInMonth.push(null);
}
}
setDateData(daysInMonth);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [year, month]);
// 日历的头部数组
const datePicker = Array.from({ length: 7 }, (_, index) => {
const day = index;
return (
<div className={css.datePickerDay} key={'head' + index}>
周
{day === 0
? '日'
: day === 1
? '一'
: day === 2
? '二'
: day === 3
? '三'
: day === 4
? '四'
: day === 5
? '五'
: '六'}
</div>
);
});
更新事件
useEffect(() => {
if (calendarEventData.length === 0 || haveUpdatedEvent) {
return;
}
console.log('calendarEventData', calendarEventData);
const newDateData: (dateUnit | null)[] = [];
dateData.forEach((unit) => {
newDateData.push(unit);
});
calendarEventData.forEach((item) => {
if (item.date.getFullYear() === year && item.date.getMonth() === month) {
// 日历事件日期在当前月
newDateData[firstDayOfWeek + item.date.getDate() - 1]?.event.push(item);
} else if (
firstDayOfWeek !== 0 &&
item.date.getDate() >= newDateData[0]!.day &&
item.date.getFullYear() === lastDayOfLastMonth.getFullYear() &&
item.date.getMonth() === lastDayOfLastMonth.getMonth()
) {
// 日历事件日期在上个月
newDateData[item.date.getDay()]?.event.push(item);
}
});
if (newDateData && newDateData.length > 0) {
setDateData(newDateData);
}
setHaveUpdatedEvent(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [calendarEventData, dateData]);
Powerd by YlBlog(玉龙博客)