打造超酷的 React 迷你日历组件,只需几步!

你好,我是小白Coding日志,一个热爱技术的程序员。在这里,我分享自己在编程和技术世界中的学习心得和体会。希望我的文章能够给你带来一些灵感和帮助。欢迎来到我的博客,一起在技术的世界里探索前行吧!

前言

现在市面上的组件库大多包含日历组件,那么它们具体是如何实现的呢?其实原理很简单,主要是通过使用Date对象的相关API。今天我们就来开发一个迷你版的日历组件。

功能分析

实现一个迷你版的日历组件页面布局:
  1. Header部分:最中间的位置用于显示当前月份,左右两边分别设置切换上月和下月的按钮。
  2. 星期显示:Header的下方依次显示星期日到星期六。
  3. Body部分:显示当前月的天数。可以根据当前月份获取这个月有多少天,以及根据当前月1号获取是星期几,这样我们就能知道这个月从哪一天开始。

为日历组件提供默认值,即当前日历组件默认渲染为当前年份和月份。此外,还需提供一个onChange方法,用于返回当前选中的日期。这样,一个基本的日历组件就完成了。

项目搭建

使用vite来创建一个React项目

npm create vite@latest calendar-mini

选择React然后选择JavaScript

image.png
切换到项目目录下cd calendar-mini,运行npm install 安装依赖,npm run dev 把项目跑起来
image.png

编码

  1. 页面基本布局

删除App.jsx中App组件中的HTML内容只保留下h1标签,

import { useState } from 'react'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <h1>mini日历组件📅</h1>
    </>
  )
}

export default App

新增一个Calendar组并提供基本样式,在App.jsx中导入组件

import React from 'react';

function Calendar() {
  return (
    <div className="calendar">
      <div className="header">
        <button>&lt;</button>
        <div>20237</div>
        <button>&gt;</button>
      </div>
      <div className="days">
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="empty"></div>
        <div className="empty"></div>
        <div className="day">1</div>
        <div className="day">2</div>
        <div className="day">3</div>
        <div className="day">4</div>
        <div className="day">5</div>
        <div className="day">6</div>
        <div className="day">7</div>
        <div className="day">8</div>
        <div className="day">9</div>
        <div className="day">10</div>
        <div className="day">11</div>
        <div className="day">12</div>
        <div className="day">13</div>
        <div className="day">14</div>
        <div className="day">15</div>
        <div className="day">16</div>
        <div className="day">17</div>
        <div className="day">18</div>
        <div className="day">19</div>
        <div className="day">20</div>
        <div className="day">21</div>
        <div className="day">22</div>
        <div className="day">23</div>
        <div className="day">24</div>
        <div className="day">25</div>
        <div className="day">26</div>
        <div className="day">27</div>
        <div className="day">28</div>
        <div className="day">29</div>
        <div className="day">30</div>
        <div className="day">31</div>
      </div>
    </div>
  );
}

export default Calendar;
.calendar {
  border: 1px solid #aaa;
  padding: 10px;
  width: 300px;
  height: 250px;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 40px;
}

.days {
  display: flex;
  flex-wrap: wrap;
}

.empty, .day {
  width: calc(100% / 7);
  text-align: center;
  line-height: 30px;
}

.day:hover {
  background-color: #ccc;
  cursor: pointer;
}
.day:hover, .selected {
  background-color: #22a4f1;
  cursor: pointer;
}
.m-10 {
  margin: 10px 0;
}
import { useState } from 'react'
import './App.css'
import Calendar from './component/Calendar'
function App() {
  return (
    <>
      <h1>mini日历组件📅</h1>
      <Calendar />
    </>
  )
}

export default App

来看看效果👇
image.png

  1. 根据当前月份获取上月下月

结合useState来获取日历组件的当前年份和月份,月份要加1因为月份是从0开始的image.png
根据当前月获取上月下月

// 上月 
  const lastMonth = () =>{
    setDate(new Date(date.getFullYear(), date.getMonth() - 1, 1))
  }
  // 下月
  const nextMonth = () =>{
    setDate(new Date(date.getFullYear(), date.getMonth() + 1, 1))
  }
  1. 根据当前月份来渲染日期
 const renderDays = () => {
    const days = []
    const daysCount = daysOfMonth(date.getFullYear(), date.getMonth())
    const firstDays = firstDayOfMonth(date.getFullYear(), date.getMonth())

    for(let i = 0; i<firstDays; i++){
      days.push(<div key={`empty-${i}`} className="empty"></div>)
    }
    for(let i = 1; i<=daysCount; i++){
      const handelClick = () =>{
        const current = new Date(date.getFullYear(), date.getMonth(), i)
        setDate(current)
        props.onChange(current)
      }
      if(i === date.getDate()){
        days.push(<div key={i} className="day selected" onClick={()=>handelClick()}>{i}</div>)
      }else{
        days.push(<div key={i} className="day" onClick={()=>handelClick()}>{i}</div>)
      }
    }
    
    return days
  }

定义一个renderDays函数用来渲染日期,提供一个days数组来动态的渲染日期,daysCount是当前月的天数,firstDays是当前月是从星期几开始的,先循环firstDays来添加空白项,再循环daysCount添加天,handelClick当点击某一天的时候选中并改变背景色。

  1. 添加默认值defaultValue和onChange方法

Calendar组件提供默认值
image.png
为state设置默认值
image.png
Calendar组件提供onChange方法,在点击某一天的时候调用onChange回调将最新的值传递给onChange方法

image.png
image.png

Calendar.jsx组件全部代码

import { useState } from 'react';

function Calendar(props) {
  const [date, setDate] = useState(new Date(props.defaultValue))
  console.log(date.toLocaleDateString())
  // 上月 
  const lastMonth = () =>{
    setDate(new Date(date.getFullYear(), date.getMonth() - 1, 1))
  }
  // 下月
  const nextMonth = () =>{
    setDate(new Date(date.getFullYear(), date.getMonth() + 1, 1))
  }
// 获取每个月有多少天
const daysOfMonth = (year, month) =>{
  return new Date(year, month + 1, 0).getDate()
}
// 获取每个月第一天是星期几
const firstDayOfMonth = (year, month) =>{
  return new Date(year, month, 1).getDay()
}
const renderDays = () => {
  const days = []
  const daysCount = daysOfMonth(date.getFullYear(), date.getMonth())
  const firstDays = firstDayOfMonth(date.getFullYear(), date.getMonth())

  for(let i = 0; i<firstDays; i++){
    days.push(<div key={`empty-${i}`} className="empty"></div>)
  }
  for(let i = 1; i<=daysCount; i++){
    const handelClick = () =>{
      const current = new Date(date.getFullYear(), date.getMonth(), i)
      setDate(current)
      props.onChange(current)
    }
    if(i === date.getDate()){
      days.push(<div key={i} className="day selected" onClick={()=>handelClick()}>{i}</div>)
    }else{
      days.push(<div key={i} className="day" onClick={()=>handelClick()}>{i}</div>)
    }
  }
  return days
}
  return (
    <div className="calendar">
      <div className="header">
        <button onClick={lastMonth}>&lt;</button>
        <div>{ date.getFullYear() }{ date.getMonth() + 1 }</div>
        <button onClick={nextMonth}>&gt;</button>
      </div>
      <div className="days">
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        <div className="day"></div>
        {renderDays()}
      </div>
    </div>
  );
}

export default Calendar;

App.jsx代码

import { useState } from 'react'
import './App.css'
import Calendar from './component/Calendar'
function App() {
  return (
    <>
      <h1>mini日历组件📅</h1>
      <Calendar defaultValue={'2024-07-19'} onChange={(date)=>{alert(date.toLocaleDateString())}} />
      <Calendar defaultValue={'2024-10-04'} /
    </>
  )
}

export default App

bandicam-2024-07-19-00-32-19-886.gif
以上就实现了一个mini版的日历组件😁
最后还是那句话:即使代码逻辑很简单,也值得记录下来。我的编程目标是避免重复造轮子!😊
如果觉得有用,就给我点个赞吧😁
探索更多有趣知识,欢迎关注我的微信公众号:小白Coding日志,每天分享精彩内容,与你一同探寻知识的边界。一起开启知识新旅程!🚀📚
关注我的技术博客,探索前沿科技与实用开发技巧。一起携手走向代码的精彩世界!🚀💻 不错过每一篇精彩!

https://www.xiaobaicoding.com

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值