基于React实现日历组件详细教程

前言

日历组件是常见的日期时间相关的组件,围绕日历组件设计师做出过各种尝试,展示的形式也是五花八门。但是对于前端开发者来讲,主要我们能够掌握核心思路,不管多么奇葩的设计我们都能够把它做出来。

本文将详细分析如何渲染一个简单的日历组件。

步骤

计算每个月中具体包含的日期

因为日历需要把当前月的每一天都展示出来,展示的前提是我们能够知道当前月具体都有哪些日子。那么如何优雅的获取每个月所有的天呢?

为了能够更方便的操作时间,我们需要引入dayjs 工具库,这也是我们手写日历组件唯一需要的工具库。

npm install dayjs

接下来我们实现一个工具方法,方法的目的是当我们传入年、月,就会返回当前月份的所有天。

import dayjs from "dayjs";export const getDaysOfMonth = (year: number, month: number) => {const firstDayOfMonth = dayjs(`${year}-${month}-1`);const lastDayOfMonth = dayjs(`${year}-${month + 1}-1`).subtract(1, "day");const days = [];let tempDate = firstDayOfMonth;while (tempDate.isBefore(lastDayOfMonth) || tempDate.isSame(lastDayOfMonth)) {days.push(tempDate);tempDate = tempDate.add(1, "day");}return days;
};

我们输出一下2023-08有哪些日子,并且简单渲染出来看看效果

function App() {const days = getDaysOfMonth(2023, 8);// 控制台打印days.forEach((day) => console.log(day.format("YYYY-MM-DD")));return (<div className="App">{days.map((day) => {return <div>{day.format('DD')}</div>;})}</div>);
}

在这里插入图片描述
在这里插入图片描述

以周为单位分组日期

  1. 首先我们先计算出日历分组标题,也就是周一,周二 … 周日
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
dayjs.locale("zh-cn");const weekTitles = useMemo(() => {return [...Array(7)].map((_, weekInx) => {return dayjs().day(weekInx);});}, []);// 日历标题渲染
<div className="calendar-title">{weekTitles.map((title) => {return <div>{title.format("ddd")}</div>;})}
</div>
  1. 对于当月所有的日期,每7天一组进行分组,也就是共分成7列
<div className="calendar-content">{days.map((day) => {return <div>{day.format("DD")}</div>;})}
</div>
  1. 加上对应的样式
.calendar {display: flex;flex-direction: column;width: 400px;
}.calendar-title {display: grid;grid-template-columns: repeat(7, 1fr);padding-bottom: 8px;
}.calendar-content {width: 100%;display: grid;grid-template-columns: repeat(7, 1fr);
}
  1. 看看效果
    在这里插入图片描述

猛的一看好像完成了,但是仔细检查会发现,2023年8月1号是周二,我们渲染出来的却是周日。这肯定是不对的,那么问题出在哪儿呢?

由于我们是通过grid布局来渲染日期数组,数组的第一位数据是8月1号,所以就成了上面图中的效果。所以我们得想办法将每个月1号前的日期也补全直到每周周日。

让我们改造一下获取日期的工具方法getDaysOfMonth ,下面代码是最终改造完成后的。

export const getDaysOfMonth = (year: number, month: number) => {let firstDayOfMonth = dayjs(`${year}-${month}-1`);let lastDayOfMonth = dayjs(`${year}-${month + 1}-1`).subtract(1, "day");**// 开始补全第一天前的日期while (firstDayOfMonth.day() !== 0) {firstDayOfMonth = firstDayOfMonth.subtract(1, "day");}// 开始补全最后一天后的日期while (lastDayOfMonth.day() !== 6) {lastDayOfMonth = lastDayOfMonth.add(1, "day");}**const days = [];let tempDate = firstDayOfMonth;while (tempDate.isBefore(lastDayOfMonth) || tempDate.isSame(lastDayOfMonth)) {days.push(tempDate);tempDate = tempDate.add(1, "day");}return days;
};

在这里插入图片描述

可以看出我们已经正确的渲染出了日历,只是样式看起来比较简陋。

日历支持切换月份

上面的结果是我固定渲染了2023年8月的日历,大多数的日历是需要支持月份切换的,甚至有的日历设计是需要支持用户上下滚动就能够显示对应的月份。我们先简单实现通过按钮点击支持日历月份的切换。

  1. 显示当前月份(日历顶部显示档期月份)
// tsx
<div className="calendar-month"><div className="calendar-month-switch">{"<"}</div><div>{month.format("MMM YYYY")}</div><div className="calendar-month-switch">{">"}</div>
</div>// css
.calendar-month {display: flex;align-items: center;justify-content: space-between;padding: 16px;
}.calendar-month-switch {display: flex;align-items: center;justify-content: center;height: 24px;width: 24px;cursor: pointer;
}

简单看看效果

在这里插入图片描述

  1. 支持点击切换月份

从上图可以看到,月份两边有两个「箭头」按钮,接下来我们在这两个按钮上绑定事件,用来切换不同的月份

// 切换月份事件,-1 代表前一个月,1代表后一个月
const onMonthSwitch = (action: number) => {setMonth((month) => {return month.add(action, "month");});};<div className="calendar-month"><div className="calendar-month-switch" onClick={()=> onMonthSwitch(-1)}>{"<"} </div> <div>{month.format("MMM YYYY")}</div><div className="calendar-month-switch" onClick={()=> onMonthSwitch(1)}>{">"}</div>
</div>

在这里插入图片描述

小节总结

本文详细的记录了一个最简单的日历组件的实现过程,感兴趣但是之前还没有实现过日历的同学可以直接下载代码试试,希望对大家有所启发。也可以直接访问https://react-calendar-training.vercel.app/看成品效果。

由于设计师脑洞的千变万化,日历的展现形式也各不相同,后续我还将继续记录更多形式的日历实现过程,感兴趣的同学敬请期待。有任何问题请留言,如果对你有帮助,请帮忙点个赞🦉

相关链接

  1. 源码

    https://github.com/levenx/react-calendar-training

  2. 在线DEMO效果

    https://react-calendar-training.vercel.app

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/51200.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ vector详解

vector是C STL库中最常用的容器类&#xff0c;实际上它是一种模板&#xff08;template&#xff09;。它支持动态扩容&#xff0c;十分方便&#xff0c;不像数组需要新建空间来扩容。 vector支持大部分类型的对象作为其元素&#xff0c;甚至组成vector的元素可以是vector&…

【Python原创毕设|课设】基于Python Flask的上海美食信息与可视化宣传网站项目-文末附下载方式以及往届优秀论文,原创项目其他均为抄袭

基于Python Flask的上海美食信息与可视化宣传网站&#xff08;获取方式访问文末官网&#xff09; 一、项目简介二、开发环境三、项目技术四、功能结构五、运行截图六、功能实现七、数据库设计八、源码获取 一、项目简介 随着大数据和人工智能技术的迅速发展&#xff0c;我们设…

解决Oracle中XML插入数据时的空格问题

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

2.含电热联合系统的微电网运行优化

含电热联合系统的微电网运行优化 MATLAB代码&#xff1a;含电热联合系统的微电网运行优化 关键词&#xff1a;微网 电热联合系统 优化调度 参考文档&#xff1a;《含电热联合系统的微电网运行优化》完全复现 仿真平台&#xff1a;MATLAB yalmipcplex [火]主要内容&#xf…

k8s 常用命令(三)

1、查看版本信息&#xff1a;kubectl version [rootmaster ~]# kubectl version [rootmaster ~]# kubectl version Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.3", GitCommit:"ca643a4d1f7bfe34773c74f7952…

antd5源码分析之classnames库

代码仓库 https://github.com/JedWatson/classnames 可以找到rec/index.js文件 var hasOwn {}.hasOwnProperty;function classNames() {var classes [];for (var i 0; i < arguments.length; i) {var arg arguments[i];if (!arg) continue;var argType typeof arg;if…

代码随想录打卡—day38—【DP】— 8.24 DP基础

1 DP理论基础 1.1 什么是DP 如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 动规是由前一个状态推导出来的&#xff0c;而贪心是局部直接选最优的。 1.2 DP解题步骤 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定状态转移公式 / 递推…

Day14-1-NodeJS后端工程化

一 前端和后端概念 前端 前端:指运用html+css+JavaScript等技术实现用户体验良好的web应用界面工作 根据设计原稿高保真实现页面及交互和后端人员协同完成项目的接口设计与编写文档。和测试人员协同工作,完成bug的修复和跟踪根据产品需求完成对应的功能(nodejs)技术栈 基础…

JAVA开发环境接口swagger-ui使用总结

一、前言 swagger-ui是java开发中生产api说明文档的插件&#xff0c;这是后端工程师和前端工程师联调接口的桥梁。生成的文档就减少了很多没必要的沟通提高开发和测试效率。 二、 swagger-ui的使用 1、引入maven依赖 <dependency><groupId>io.springfox</grou…

cpolar+JuiceSSH实现手机端远程连接Linux服务器

文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …

keepalived+lvs(DR)(四十六)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、作用 二、调度器配置 三、web节点配置 一、作用 使用keepalived解决lvs的单点故障 高可用集群 二、调度器配置 安装keepalived yum install -y k…

评测PlayStation Portal:价格、设计、连接选项等

PlayStation Portal发出PlayStation手持设备返回的信号。这款新设备最初在2023年3月的PlayStation Showcase上被宣布为Project Q&#xff0c;它将允许你通过强大的Wi-Fi信号在任何地方播放最好的PS5游戏。 虽然PlayStation Portal可能不是PlayStation Portable和PlayStation G…

leetcode几个数组题

数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合 因为数组的在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添元素的时候&#xff0c;就难免要移动其他元素的地址 二分查找 移除元素 有序数组的平方 209.长度最小的子数组

Ubuntu22.04部署OpenStack-zed all-in-one

一.环境准备 本文以VMWare中创建的虚拟机为例 硬件 规格 备注 网卡1 已分配内网IP,以ens33为例 网卡2 不需要分配IP,以ens37为例 系统盘 60G 用于安装系统及挂载存储 数据盘 100G 初始不需要分区,后续操作用 二.开启ROOT远程登录 使用现有账号…

Mybatis查询in的字段过多不走索引

mybatis查询in的字段有索引&#xff0c;比如说是主键查询&#xff0c; 但是in的字段过多导致索引失效&#xff0c; 这个时候可以考虑将in的数量变少&#xff0c; 200以内都可以&#xff0c; 在数据库方面采用 foreach unionall 的方式将数据集合查询出来 Service层: List<…

jenkins 日志输出显示时间戳的方式

网上很多方式比较片面&#xff0c;最新版插件直接使用即可无需更多操作。 使用方式如下&#xff1a; 1.安装插件 Timestamper 2.更新全局设置 系统设置-找到 Timestamper 勾选 Enabled for all Pipeline builds 也可修改时间戳格式。 帮助信息中显示 When checked, timesta…

马原——5.两大总特征(辩证法)

两大总特征是解释了世界是怎样存在的。 三大规律是对两大总特征的进一步细化 对立统一规律解释了世界是怎样联系的&#xff0c;为什么发展 量变质变规律解释了怎样发展 否定之否定规律那里发展 五对基本范畴解释了联系和发展环节上的逻辑 客观性&#xff1a;不以人的意志为转…

使用easyExcel导入导出Date类型的转换问题

起因&#xff1a;在业务需求上需要将Excel表中的日期导入&#xff0c;存储到数据库中&#xff0c;但是entity中的日期类型使用Date来接收&#xff0c;这样导致时间精确到秒。这时&#xff0c;即使使用DateTimeFormat("yyyy-MM-dd")也无法成功转换&#xff0c;会报如下…

前端面试:【前端工程化】CommonJS 与 ES6 模块

嗨&#xff0c;亲爱的前端开发者&#xff01;在现代Web开发中&#xff0c;模块化是构建可维护和可扩展应用程序的关键。本文将深入探讨两种主要的JavaScript模块系统&#xff1a;CommonJS 和 ES6 模块&#xff0c;以帮助你了解它们的工作原理、用法以及如何选择合适的模块系统。…

SpringBoot案例-配置文件-@ConfigurationProperties

问题分析 在往期的配置参数的文章中&#xff0c;对于阿里云OSS的参数时设置在yml配置文件中&#xff0c;然后使用Value&#xff08;”${}“&#xff09;对参数进行赋值&#xff0c;具体如下&#xff1a; 此种方法比较繁琐 问题解决 使用注解 Data 为变量自动生成get/set方…