howenhuo

2018-12-09 18:54

如何使用 React 构建自定义日期选择器(3)

本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载

接着上一篇:如何使用 React 构建自定义日期选择器(2)

Datepicker 组件

构建 Datepicker 组件

要开始构建 Datepicker 组件,请将以下代码片段添加到 src/components/Datepicker/index.js 文件。

import React from "react";
import PropTypes from "prop-types";
import Calendar from "../Calendar";
import * as Styled from "./styles";
import { isDate, getDateISO } from "../../helpers/calendar";

class Datepicker extends React.Component {

  state = { date: null, calendarOpen: false }

  toggleCalendar = () => this.setState({ calendarOpen: !this.state.calendarOpen })

  handleChange = evt => evt.preventDefault()

  handleDateChange = date => {
    const { onDateChanged } = this.props;
    const { date: currentDate } = this.state;
    const newDate = date ? getDateISO(date) : null;

    currentDate !== newDate &&
      this.setState({ date: newDate, calendarOpen: false }, () => {
        typeof onDateChanged === "function" && onDateChanged(this.state.date);
      });
  }

  componentDidMount() {
    const { value: date } = this.props;
    const newDate = date && new Date(date);

    isDate(newDate) && this.setState({ date: getDateISO(newDate) });
  }

  componentDidUpdate(prevProps) {
    const { value: date } = this.props;
    const { value: prevDate } = prevProps;
    const dateISO = getDateISO(new Date(date));
    const prevDateISO = getDateISO(new Date(prevDate));

    dateISO !== prevDateISO && this.setState({ date: dateISO });
  }

}

Datepicker.propTypes = {
  label: PropTypes.string,
  value: PropTypes.string,
  onDateChanged: PropTypes.func
}

export default Datepicker;

在这里,组件 state 初始化为两个属性:

  • date:一个 ISO string,表示日期选择器的当前日期。格式是 “YYYY-MM-DD”
  • calendarOpen :一个 boolean 标记,表示日期选择器的日历是否可见。

当组件 mount 时,Date 对象从传递给组件 props 的 value 解析,并更新 state,如componentDidMount() 方法所示。

handleDateChange() 方法以 Date 对象作为参数,并更新 state 下的 date。如果 Datepicker 组件的 props 传递了 onDateChanged 回调函数,则将使用更新的 ISO 日期字符串调用该函数。

渲染 datepicker

此时,值得一提的是,Bootstrap Dropdown 组件将用于模拟自定义日期选择器的下拉效果。这就是为什么 Reactstrap 包被添加为此项目的依赖项的原因。

正如您很快会注意到,在日期选择器中渲染的样式化组件是 Reactstrap 下拉组件的样式扩展。

更新 Datepicker 组件以包含 render() 方法,如下面的代码片段所示。

class Datepicker extends React.Component {

  // ... other methods here

  render() {
    const { label } = this.props;
    const { date, calendarOpen } = this.state;

    return (
      <Styled.DatePickerContainer>

        <Styled.DatePickerFormGroup>

          <Styled.DatePickerLabel>{label || 'Enter Date'}</Styled.DatePickerLabel>

          <Styled.DatePickerInput
            type="text"
            value={date ? date.split("-").join(" / ") : ""}
            onChange={this.handleChange}
            readOnly="readonly"
            placeholder="YYYY / MM / DD"
          />

        </Styled.DatePickerFormGroup>

        <Styled.DatePickerDropdown isOpen={calendarOpen} toggle={this.toggleCalendar}>

          <Styled.DatePickerDropdownToggle color="transparent" />

          <Styled.DatePickerDropdownMenu>
            { calendarOpen && (
              <Calendar date={date && new Date(date)} onDateChanged={this.handleDateChange} />
            )}
          </Styled.DatePickerDropdownMenu>

        </Styled.DatePickerDropdown>

      </Styled.DatePickerContainer>
    );
  }

}

Styled.DatePickerFormGroup 组件是一个 Bootstrap 的 .form-group,它包装日期选择器标签和输入字段。需要注意的是,输入字段的类型是 “text”,并且标记为 readonly,这样就无法直接编辑它。还要注意,输入元素上的 change 事件的默认行为已经被阻止。

Styled.DatePickerDropdown 组件及其后代,是 ReactstrapDropdown 组件的样式扩展。您可以在 这里 了解更多关于 Reactstrap 下拉列表的信息。

最后,Calendar 组件在下拉菜单中渲染,传递 state 中的 dateonDateChanged 回调函数的handleDateChange() 方法。

Datepicker 组件最终渲染的 DOM 应该如下所示(带有一些样式):

设置日期选择器的样式

将以下代码片段添加到 src/components/Datepicker/styles.js,以创建日期选择器所需的样式组件。

import styled from 'styled-components';
import { FormGroup, Label, Input, Dropdown, DropdownToggle, DropdownMenu } from 'reactstrap';

export const DatePickerContainer = styled.div`
  position: relative;
`;

export const DatePickerFormGroup = styled(FormGroup)`
  display: flex;
  justify-content: space-between;
  position: relative;
  width: 100%;
  border: 2px solid #06c;
  border-radius: 5px;
  overflow: hidden;
`;

export const DatePickerLabel = styled(Label)`
  margin: 0;
  padding: 0 2rem;
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: #06c;
  border-right: 2px solid #06c;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 102, 204, 0.05);
`;

export const DatePickerInput = styled(Input)`
  padding: 1rem 2rem;
  font-weight: 500;
  font-size: 1rem;
  color: #333;
  box-shadow: none;
  border: none;
  text-align: center;
  letter-spacing: 1px;
  background: transparent !important;
  display: flex;
  align-items: center;
  ::placeholder {
    color: #999;
    font-size: 0.9rem;
  }
`;

export const DatePickerDropdown = styled(Dropdown)`
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
`;

export const DatePickerDropdownToggle = styled(DropdownToggle)`
  position: relative;
  width: 100%;
  height: 100%;
  background: transparent;
  opacity: 0;
  filter: alpha(opacity=0);
`;

export const DatePickerDropdownMenu = styled(DropdownMenu)`
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  border: none;
  padding: 0;
  margin: 0;
  transform: none !important;
`;

应用程序组件

最后,更新 src/App.js 文件,看起来像下面的代码片段。

import React, { Component } from "react";
import Datepicker from "./components/Datepicker";

class App extends Component {
  render() {
    return (
      <div className="w-25 py-5 my-5 mx-auto">
        <Datepicker label="Birthday" value="2000-08-15" />
      </div>
    );
  }
}

export default App;

如果您按照本文和代码片段进行操作,则应该在 React 应用程序中渲染出一个可用的自定义日期选择器。

结论

在本教程中(123),您已经能够逐步了解如何构建一个定制的 React 日期选择器组件,该组件可以作为原生 HTML5 日期选择器输入元素的替代。

虽然本教程中创建的自定义日期选择器能按预期工作,但它并不能完全满足日期选择器元素的所有要求。可以进一步改进,例如:

  • 通过 props 实现 maxmin 日期
  • 将输入类型从 “text” 切换到 “date”
  • 更好的可访问性改进

你可以在 react-datepicker-demo 的 GitHub 上获得这个自定义日期选择器的更多改进版本的完整源代码。 您还可以在 Code Sandbox 上查看演示。

0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。