我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么用React撸一个日程组件

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么用React撸一个日程组件

本篇内容主要讲解“怎么用React撸一个日程组件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用React撸一个日程组件”吧!

目录结构

└─Calendar
    │  data.d.ts  类型定义文件
    │  index.tsx  入口文件
    │
    ├─components
    │  ├─CalendatrHeader 头部容器组件
    │  │  │  index.less
    │  │  │  index.tsx
    │  │  │
    │  │  └─components
    │  │      ├─DailyOptions 顶部切换日期和切换模式状态组件
    │  │      │      index.less
    │  │      │      index.tsx
    │  │      │
    │  │      └─WeeklyOptions 周模式日期和星期组件
    │  │              index.less
    │  │              index.tsx
    │  │
    │  ├─Container 容器组件
    │  │      Container.tsx
    │  │      index.less
    │  │
    │  ├─ScheduleCantainer 下半部日程容器
    │  │      index.less
    │  │      index.tsx
    │  │
    │  └─ScheduleItem 灰色部分每一条日程组件
    │          index.less
    │          index.tsx
    │
    └─utils
            index.ts 工具文件

?拆分组件

仔细看图, 不难看出, 组件大块上我拆分成了三个部分:
Container容器: 该组件是整个组件的容器, 负责UI核心状态数据, 维护两个状态:

  1. targetDay: 当前选中日期时间戳(为什么选用时间戳后续解释);

  2. switchWeekAndDay: 保存日和周的状态;

CalendatrHeader头部容器组件: Container容器的子组件, 该组件负责切换日期, 改变组件周和日的状态; 该组件内, 包含日历组件, 星期组件, 日期筛选组件, 日和周切换组件, 今天按钮组件, 最后还有一个业务组件的容器(businessRender);

ScheduleCantainer日程容器组件: 该组件被25 (因为是从今天0点到次日凌晨0点的区间) 个scheduleRender组件撑开, 子组件还包括时间刻度组件;

scheduleRender: 特意说一下这个组件, 这个组件接受一个回调, 回调会返回一个JSX, 这个JSX就是调用者传入的自定义样式的日程组件(具体内容在后文讲吧);

这就是大致的组件拆分, 文字表达确实欠佳, 可以结合图片YY;

接下来就开干吧!!!

代码实现

先看一下接受的参数类型定义:

type dataType = {  startTime: DOMTimeStamp; // 开始时间戳  endTime: DOMTimeStamp; // 结束时间戳  [propsName: string]: any; // 业务数据};type ContainerType = {  data: dataType[]; // 业务数据  initDay?: DOMTimeStamp; // 初始化时间戳  onChange?: (params: DOMTimeStamp) => void; // 改变日期时的onChange方法  height?: number; // ScheduleCantainer容器的高度  scheduleRender?: ({    data: dataType,    timestampRange: [DOMTimeStamp, DOMTimeStamp],  }) => JSX.Element; // 传入的回调, 会接收到当前这条数据的业务数据, 当前业务数据所在的时间戳范围;  businessRender?: ({ timestamp: DOMTimeStamp }) => React.ReactNode; // 传入的业务组件, 查询前端蔡徐坤那个, 看图, 想起来了吗?  mode?: "day" | "week"; // 初始化展示日和天的模式};

Container容器组件

代码:

const Container: React.FC<ContainerType> = ({  initDay,  onChange,  scheduleRender,  businessRender,  data,  height = 560,  mode = "day",}) => {  // 当前选择日期时间戳  const [targetDay, setTargetDay] = useState<DOMTimeStamp>(initDay);  // 切换日和周  const [switchWeekandDay, setSwitchWeekandDay] = useState<"day" | "week">(mode);  return (    <div className={style.Calendar_Container}>      <CalendatrHeader        targetDay={targetDay}        setTargetDay={(timestamp) => {          onChange(timestamp);          setTargetDay(timestamp);        }}        businessRender={businessRender}        switchWeekandDay={switchWeekandDay}        setSwitchWeekandDay={setSwitchWeekandDay}      />      <ScheduleCantainer        height={height}        data={data}        targetDay={targetDay}        scheduleRender={scheduleRender}      />    </div>  );};

看代码可以思考一下, 肯定要将全局的状态数据提升到最高层级去控制, 也符合React的组件设计哲学;

维护了当前时间戳和日/周的状态, 所有子组件的状态都是根据targetDay去展示的;

CalendatrHeader头部容器组件

头部容器我觉得其他的还好, 由于星期是写死的(主要是参考了一下苹果的那个日程组件, 苹果的星期就没换, 所以参考大厂优秀的设计), 所以比较敲脑壳的就是如何能准确的展示一周的日期;

其实展示一周的日期我写了两种方式:

第一种是以当前的日期的星期为基准, 分别向前和向后去计算, 最后输出一个[29, 30, 31, 1, 2, 3, 4]这样的List, 如果恰巧今天是1号 或者 2号, 就去拉去上个月最后一天的日期往前递减;

第二种方式就是下面代码的方式, 也是拿到当前日期的星期去定位, 通过时间戳去动态计算出来, 只要知道往前减几天, 往后追加几天就好了;

其实两种方式都可以, 最后我用了第二种, 显然第二种更加简洁;

如下图:

怎么用React撸一个日程组件

 当前一周就要输出[12, 13, 14, 15, 16, 17, 18]

下面是上述难点具体实现的代码:

const calcWeekDayList: (params: number) => WeekType = (params) => {    const result = [];    for (let i = 1; i < weekDay(params); i++) {      result.unshift(params - 3600 * 1000 * 24 * i);    }    for (let i = 0; i < 7 - weekDay(params) + 1; i++) {      result.push(params + 3600 * 1000 * 24 * i);    }    return [...result] as WeekType;  };

代码:

const CalendatrHeader: React.FC<CalendatrHeaderType> = ({  targetDay,  setTargetDay,  switchWeekandDay,  businessRender,  setSwitchWeekandDay,}) => {  // 当前一周的日期  const [dateTextList, setDateTextList] = useState<WeekType | []>([]);  // 这个状态是在切换周的时候, 直接增加或者减少一周的时间戳, 下一周或者上一周的日期就会被自动算出来;  const [currTime, setCurrTime] = useState<number>(targetDay);   useEffect(() => {    setDateTextList(calcWeekDayList(targetDay));  }, [targetDay]);  // 根据当前时间戳, 计算之前和之后天数的日期, 由于星期是固定不变的, 所以只计算当前一周的日期就好了  const calcWeekDayList: (params: number) => WeekType = (params) => {    const result = [];    for (let i = 1; i < weekDay(params); i++) {      result.unshift(params - 3600 * 1000 * 24 * i);    }    for (let i = 0; i < 7 - weekDay(params) + 1; i++) {      result.push(params + 3600 * 1000 * 24 * i);    }    return [...result] as WeekType;  };  const onChangeWeek: (type: "prevWeek" | "nextWeek", switchWay: "week" | "day") => void = (    type,    switchWay,  ) => {    if (switchWay === "week") {      const calcWeekTime =        type === "prevWeek" ? currTime - 3600 * 1000 * 24 * 7 : currTime + 3600 * 1000 * 24 * 7;      setCurrTime(calcWeekTime);      setDateTextList([...calcWeekDayList(calcWeekTime)]);    }    if (switchWay === "day") {      const calcWeekTime =        type === "prevWeek" ? targetDay - 3600 * 1000 * 24 : targetDay + 3600 * 1000 * 24;      setCurrTime(calcWeekTime);      setTargetDay(calcWeekTime);    }  };  return (    <div className={style.Calendar_Header}>      <DailyOptions        targetDay={targetDay}        setCurrTime={setCurrTime}        setTargetDay={setTargetDay}        dateTextList={dateTextList}        switchWeekandDay={switchWeekandDay}        setSwitchWeekandDay={(value) => {          setSwitchWeekandDay(value);          if (value === "week") {            setDateTextList(calcWeekDayList(targetDay));          }        }}        onChangeWeek={(type) => onChangeWeek(type, switchWeekandDay)}      />      {switchWeekandDay === "week" && (        <WeeklyOptions          targetDay={targetDay}          setTargetDay={setTargetDay}          dateTextList={dateTextList}        />      )}      <div className={style.Calendar_Header_businessRender}>        <div className={style.Calendar_Header_Zone}>GMT+8</div>        {businessRender({ timestamp: targetDay })}      </div>    </div>  );};

DailyOptions : 其实就是头部切换"一周日期" & "日和周模式" & "今天"的组件的容器;

WeeklyOptions : 这个是下面展示当前一周星期几和日期的组件, 如果切换为day的话不展示

businessRender: 这个就是肖战哥哥那一栏用户传入的业务组件;

ScheduleCantainer详细日程容器

左侧刻度

左侧刻度其实就是写死的 从00:00 - 01:00 ---> 23:00 - 00:00, 但是在写的时候有一个小的问题, 就是这个组件是浮动到左侧的, 而且他要随着右侧条目的滚动而滚动, 其实一开始我写到一个盒子里了, 滚动容器整体就一起滚动了, 但是遇到了一个小问题, 由于右侧条目会变得超宽, 就会出现横向滚动条, 如果横滚整个容器的话, 左侧的时间刻度就会被滚动出可视区域.

所以还是绝对定位之后, 监听右侧日程条目的滚动事件, 动态的改变左侧的style的top值, 反向赋值就好了, 由于是向下滚动的, 所以左侧的时间刻度需要向上滚动, 所以top值取反就会达到同步的效果; 真是个小机灵鬼吧, 嘿嘿; 这个代码就不占用篇幅了, 大家自由发挥吧, 如果有更好的方式, 欢迎评论区留言.

ScheduleItem日程容器条目

先看下这个组件的代码:

const ScheduleItem: React.FC<ScheduleItemType> = ({  timestampRange,  dataItem,  scheduleRender,  width,  dataItemLength,}) => {  // 计算容器高度  const calcHeight: (timestampList: [number, number]) => number = (timestampList) =>    timestampList.length > 1 ? (timestampList[1] - timestampList[0]) / 1000 / 60 / 2 : 30;  const calcTop: (startTime: number) => number = (startTime) => moment(startTime).minute() / 2;  // 计算 ScheduleItem 宽度  const calcWidth: (w: number, d: number) => string = (w, d) =>    width === 0 || dataItemLength * width < 347 ? "100%" : `${d * w}px`;  return (    <div style={{ position: "relative" }} className={style.Calendar_ScheduleItem_Fath}>      <div        className={style.Calendar_ScheduleItem}        style={{ width: calcWidth(width, dataItemLength) }}      >        {dataItem.map((data, index) => {          return (            <Fragment key={index}>              {data.startTime >= timestampRange[0] && data.startTime < timestampRange[1] && (                <div                  className={`${style.Calendar_ScheduleItem_container} Calendar_ScheduleItem_container`}                  style={{                    height: `${calcHeight([data.startTime, data.endTime]) || 30}px`,                    top: calcTop(data.startTime),                  }}                >                  {scheduleRender({ data, timestampRange })}                </div>              )}            </Fragment>          );        })}      </div>    </div>  );};

这一部分呢(就是下面灰色一条一条的部分), 为什么要单独出一个组件呢? 可以先思考一下......

好了, 不卖关子了, 其实就是为了好定位用户的日程数据, 例如今天的10:00 -- 11:00, 定位到哪里的问题.

还记得这个API吗?

scheduleRender?: ({    data: dataType,    timestampRange: [DOMTimeStamp, DOMTimeStamp],  }) => JSX.Element;

这个组件内会有[DOMTimeStamp, DOMTimeStamp] 这样的一个参数(DOMTimeStamp时间戳的意思), 这两个时间戳其实就是当前时段的 10:00 -- 11:00 的其实和截至时间戳, 由于我们接受的startTime和endTime也是时间戳, 通过比较大小是否在这个范围, 就可以控制展示和隐藏, 这回明白为什么采用时间戳了吧, 直接比较数字大小就好了;

我们再说一下这个东东的样式问题:

其实这个东东我我写死了30px, 原因呢就是因为一小时是60分钟, 如果60px的话太高了, 所以写了30px, 方便定位嘛, 毕竟我懒, 不想太复杂的计算;

所以定位计算也就一行代码: const calcTop: (startTime: number) => number = (startTime) => moment(startTime).minute() / 2; 高度定位问题结了! 哈哈~~

接下来呢, 还有一个问题就是高度问题, 如图:

怎么用React撸一个日程组件

高度计算其实也不难, 主要根据当前起止时间的区间范围去计算( 1px 两分钟 ), 具体实现看代码:

const calcHeight: (timestampList: [number, number]) => number = (timestampList) =>    timestampList.length > 1 ? (timestampList[1] - timestampList[0]) / 1000 / 60 / 2 : 30;

首先会判断入参的时间戳是不是只有一个时间, 如果只有开始时间, 没有结束时间, 写死30px, 如果有起止时间, 就去转成分钟动态计算一下;

最后还有一个问题, 业务数据是怎么传进去是如何渲染到组件的:

先看一下我们传入data字段的JSON:

[  {    startTime: 1626057075000, // 开始时间    endTime: 1626070875000, // 结束时间    value: "any", // 业务数据  },  {    startTime: 1626057075000,    endTime: 1626070875000,    value: "any",  },  {    startTime: 1626057075000,    endTime: 1626070875000,    value: "any",  },  {    startTime: 1626057075000,    endTime: 1626070875000,    value: "any",  },];

其实我们在循环渲染这个ScheduleItem组件的时候, 用那个写死的24h的list去循环, 之后, 循环的时候, 动态的去业务数据中去查找符合当次循环的时间范围内的业务数据, 把这个数据塞到组件内; 大致代码如下:

for (let i = 0; i < HoursList.length; i++) {      resule.push({        timestampRange: [todayTime + i * 3600 * 1000, todayTime + (i + 1) * 3600 * 1000],        dataItem: [ // 由于当前一个时间段, 日程可能冲突, 所以要有一个list传入组件          ...data.filter((item) => {            return (              item.startTime >= todayTime + i * 3600 * 1000 &&              item.startTime < todayTime + (i + 1) * 3600 * 1000            );          }),        ],      });    }

到此,相信大家对“怎么用React撸一个日程组件”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么用React撸一个日程组件

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么用React撸一个日程组件

本篇内容主要讲解“怎么用React撸一个日程组件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用React撸一个日程组件”吧!目录结构└─Calendar │ data.d.ts
2023-06-05

使用react怎么实现一个Radio组件

使用react怎么实现一个Radio组件?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。测试组件:class Test extends Component { constr
2023-06-14

怎么使用react实现一个tab组件

使用react实现一个tab组件的方法:1、通过“export default props => {...}”方式创建TAB button组件;2、通过“tab-group-layout.js”组件来传“tabIndex”,并设置默认选中的tab效果;3、用react继承“react.component”组件里的onMouseOver和OnMouseOut方法即可。
2022-11-22

iOS使用核心的50行代码撸一个路由组件

目录组件化和路由路由的实现路由注册实现客户端的使用一些小想法使用组件化是为了解耦处理,多个模块之间通过协议进行交互。而负责解析协议,找到目的控制器,或者是返回对象给调用者的这个组件就是路由组件。本文讲解如何使用核心的50行代码实现一个路由组
2022-05-31

怎么使用Vite+React搭建一个开源组件库

今天小编给大家分享一下怎么使用Vite+React搭建一个开源组件库的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。目标开发环
2023-07-02

React怎么实现一个倒计时hook组件

这篇“React怎么实现一个倒计时hook组件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“React怎么实现一个倒计时ho
2023-07-05

如何使用react实现一个tab组件

这篇“如何使用react实现一个tab组件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何使用react实现一个tab组件
2023-07-04

怎么在React中利用Form组件实现一个登录功能

本篇文章给大家分享的是有关怎么在React中利用Form组件实现一个登录功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。引入所需的 Antd 组件,代码如下所示:import
2023-06-14

怎么在Html5中实现一个react拖拽排序组件

今天就跟大家聊聊有关怎么在Html5中实现一个react拖拽排序组件,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。第一步是先了解H5拖放的相关属性,MDN上有详细的说明,链接为htt
2023-06-09

怎么封装一个vue自定义日历组件

这篇文章主要介绍“怎么封装一个vue自定义日历组件”,在日常操作中,相信很多人在怎么封装一个vue自定义日历组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么封装一个vue自定义日历组件”的疑惑有所帮助!
2023-07-05

React函数组件与类组件怎么使用

这篇“React函数组件与类组件怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“React函数组件与类组件怎么使用”文
2023-06-30

基于React封装一个组件的方法是什么

这篇文章主要讲解了“基于React封装一个组件的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“基于React封装一个组件的方法是什么”吧!antd 是如何封装组件的仓库地址divi
2023-06-29

JavaScript中React面向组件编程怎么使用

这篇文章主要介绍“JavaScript中React面向组件编程怎么使用”,在日常操作中,相信很多人在JavaScript中React面向组件编程怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaS
2023-07-05

详解怎么使用vue封装一个自定义日历组件

怎么开发一个自定义日历的vue组件,下面本篇文章就手把手教你如何封装一个自定义日历组件,希望对大家有所帮助!
2023-05-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录