基于React的前端日历组件编写教程

标签

React

前端

日历

html

typescript

发布时间:

本文字数:531 字 阅读完需:约 2 分钟

前言

前端日历组件可以在很多组件库中找到,但是如果有一些更加自定义的需求,那么就需要我们从0开始写一个全新的日历组件,这样的话自定义更加方便一些。

思路

首先,我们的日历要借助js的date相关的方法。比如

  1. 当前月份的第一天: const firstDayOfMonth = new Date(year, month, 1);
  2. 当前月份的最后一天: const lastDayOfMonth = new Date(year, month + 1, 0);
  3. 上个月份的最后一天: const lastDayOfLastMonth = new Date(year, month, 0);
  4. 第一天是星期几: 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]);