手撸React组件「TimePicker」

1,360 阅读4分钟

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

Consic是一个简单易用轻量级React组件库
本文记录了我从零开始如何实现TimePicker组件的结构代码以及功能代码
附上组件源码链接!!!

写在前面

开始接触前端时主要用的都是组件库(element-ui/antd-design),在自己封装组件的过程发现,这也是一件非常有趣的事(或者说是开源的乐趣??哈哈哈)。如果下面的代码中有什么不好的地方希望大家能够帮忙指出!!(我会虚心接受滴)👂

组件展示

image.png

开始码代码!

静态结构展示

分析可以看到,组件的时间面板分别由时分秒三列组成,分别的时间范围是
时(00~24) 分(00~60) 秒(00~60) 分别,分别可以上下滑动,有三个特殊的状态分别是激活 悬浮(hover) 禁用

时分秒数组辅助函数

三列采用div>div*3>span(24|60)的结构进行生成,时分秒的数字展示其实就是长度为24|60的的数组下标转z换过来的,因为有index<10时补零的要求,所以我写了一个小的工具函数来辅助生成对应的数组。代码如下:

const getIdxArr = (length: number) => {
  const arr = new Array(length).fill('');
    return arr.map((_, i) => (i < 10 ? '0' + i : String(i)));
};

生成时分秒对应的渲染数组:

const HOUR_LIST = getIdxArr(24);
const MIN_AND_SEC_LIST = getIdxArr(60);

主要tsx代码:

<div>
  {HOUR_LIST.map((_) => (
    <span
      className={`${Number(_) === hour ? 'active' : ''} ${
        disableHour !== undefined && disableHour(_) ? 'disable' : ''
      }`}
      key={_}
    >
      {_}
    </span>
  ))}
</div>
<div>
  {MIN_AND_SEC_LIST.map((_) => (
    <span
      className={`${Number(_) === min ? 'active' : ''} ${
        disableMin !== undefined && disableMin(_) ? 'disable' : ''
      }`}
      key={_}
    >
      {_}
    </span>
  ))}
</div>
<div>
  {MIN_AND_SEC_LIST.map((_) => (
    <span
      className={`${Number(_) === second ? 'active' : ''} ${
        disableSecond !== undefined && disableSecond(_) ? 'disable' : ''
      }`}
      key={_}
    >
      {_}
    </span>
  ))}
</div>

主要的css代码需要实现的功能:

  1. 让时分秒对应的三个div从左往右排以及div中的span竖着排列
  2. div限定高度出现滚动条
  3. 隐藏滚动条的显示
 //此代码已经精简过
  display: flex;
  //限定高度
  height: 180px;
  div {
    display: flex;
    flex: 1;//div均分宽度
    flex-direction: column;//div竖着排列
    max-height: 200px;//设置最大高度,才能显示滚动条(小坑)
    overflow: auto;//出现滚动功能的核心代码
    span {
      display: flex;
      margin: 2px;
    }
    //以下代码能够隐藏滚动条
    &::-webkit-scrollbar {
      display: none;
    }
  }
}

功能代码设计

时分秒初始化

const [hour, setHour] = useState(0);
const [min, setMin] = useState(0);
const [second, setSecond] = useState(0);

active类名与数据的绑定

只要需要简单的判断hour|min|second是否与map渲染时的的内容Number(_)相等,相等即为当前选中类名。代码实现:

className={`${Number(_) === hour ? 'active' : ''}`}

disable类名与数据的校验

此处对时分秒的校验需要用户分别传入三个对应的函数,函数默认传入参数是00~24|60String类型参数,需要用户返回boolean结果,true禁用false相反,代码实现:

className={`${disableHour !== undefined && disableHour(_) ? 'disable' : ''}`}
className={`${disableMin !== undefined && disableMin(_) ? 'disable' : ''}`}
className={`${disableSecond !== undefined && disableSecond(_) ? 'disable' : ''}`}

点击时span元素自动触顶

实现效果

image.png

分析

通过查阅资料知道可以通过scrollTo函数进行实现

Window.scrollTo() 方法


定义和用法

scrollTo() 方法可把内容滚动到指定的坐标。

语法

scrollTo(xpos,ypos)
参数描述
xpos必需。要在窗口文档显示区左上角显示的文档的 x 坐标。
ypos必需。要在窗口文档显示区左上角显示的文档的 y 坐标。

思路

span元素移动到距离父级div的顶部,相关抽象代码parentDom.scrollTo(0,childrenDom.offsetTop),因此现在要解决的就是如何确定该点击事件是时|分|秒那个父级div触发的,以及是div中哪个span子元素应该进行移动。 第一个问题我使用了三个useEffect副作用hookhour|min|second三个state分别进行监听,在我封装的scrollTo方法中,传入divindex值,通过index获取到divDom,再通过选择器函数querySelector,选中类名为.activespan元素。代码如下:

scrollTo()函数

const scrollTo = (eventIdx: number, idx: number): boolean => {
  const divDom = document.querySelectorAll('.time-panel div')[idx];
  divDom.scrollTo(0, divDom.querySelector(`.active`).offsetTop - 7);
  return true;
};

hour|min|second监听hook

useEffect(() => {
  scrollTo(hour, 0);
}, [hour]);
useEffect(() => {
  scrollTo(min, 1);
}, [min]);
useEffect(() => {
  scrollTo(second, 2);
}, [second]);

赋值给Input组件

点击确定按钮,不止需要将选中的值设置到Input组将中,还需要触发用户可能定义的changeCallback函数

changeTime函数

函数代码如下:

const changeTime = (newHour: number = hour, newMin: number = min, newSecond: number = second) => {
  const time = `${newHour < 10 ? '0' + newHour : String(newHour)}:${
    newMin < 10 ? '0' + newMin : String(newMin)
  }:${newSecond < 10 ? '0' + newSecond : String(newSecond)}`;
  changeCallback && changeCallback(time);
  setTimeValue(time);
};

Input组件

<Input
  placeholder={placeholder}
  defaultValue={timeValue}
  type="primary"
  showClear={showClear}
  clearCallback={handleClear}
/>

结语

大一刚接触组件库时,往往都是直接copy了官网的示例应用到自己的项目中,有时出现了一些与自己想法不符的代码行为,我就会陷入一种很困顿的情境。现在,通过自己封装组件的这一过程,能够加深组件的一些行为的理解。也希望明年步入大三的我能够在暑期中有更多的进步!!(也希望能够找到找到心仪的实习!!!)

源码链接

源码链接

线上文档

最后,不妨给给Concis点个star⭐再走啊!!!

系列链接
手撸你的第一个组件
手撸React组件「TimePicker」
手撸React组件「Upload」文件上传