带有 Formik 表单的 React-datepicker

IT技术 reactjs formik react-datepicker
2021-04-22 00:49:58

我正在尝试以 Formik 形式使用 react-datepicker。

我有:

import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";


class Fuate extends React.Component {
    state = {
        dueDate: new Date()

    }

<Formik
                initialValues={initialValues}
                validationSchema={Yup.object().shape({
                    title: Yup.string().required("A title is required "),
                })}

                onSubmit={this.handleSubmit}

                render={({ 
                    errors, 
                    status, 
                    touched, 
                    setFieldValue,
                    setFieldTouched, 
                    handleChange, 
                    handleBlur,
                    handleSubmit, 
                    isSubmitting, 
                    dirty, 
                    values 
                }) => {

                return (
                    <div>
            ...

<DatePicker
                                            name={'dueDate'}
                                            value={values['dueDate']}
                                            onChange={e => setFieldValue('dueDate', e)}
                                        />
                                        <DatePicker
                                        style={{ width: 180 }}
                                        date={values.dueDate}
                                        mode="date"
                                        format="YYYY-MM-DD"
                                        minDate={Date.now.toString()}
                                        maxDate="2050-06-01"
                                        confirmBtnText="Confirm"
                                        cancelBtnText="Cancel"
                                        showIcon={false}
                                        customStyles={{
                                            dateInput: {
                                            marginLeft: 0,
                                            borderColor: "#fff"
                                            }
                                        }}
                                        onDateChange={date => setFieldValue("dueDate", date)}
                                        onTouch={setFieldTouched}
                                        />

对于这两个选项,表单呈现,我可以在日历上选择一个日期,但它不会出现在框中,并且状态值不会随着选择而更新。

控制台中没有错误,但警告说:

从 v2.0.0-beta.1 开始 date-fns 不接受字符串作为参数。请用于parseISO解析字符串。见: toDate @ index.js:45

我尝试制作初始状态:

dueDate: new Date().toISOString(),

但这没什么区别。

我已经看到很多关于使用 Antd 的日期选择器进行设置的帖子,但找不到有关如何使用 react-datepicker 进行设置的说明。

6个回答

更新Dani Vijay 的回答
这种用途useFielduseFormikContext从Formik V2,简化了组件的使用。

日期选择器.jsx:

import React from "react";
import { useField, useFormikContext } from "formik";
import DatePicker from "react-datepicker";

export const DatePickerField = ({ ...props }) => {
  const { setFieldValue } = useFormikContext();
  const [field] = useField(props);
  return (
    <DatePicker
      {...field}
      {...props}
      selected={(field.value && new Date(field.value)) || null}
      onChange={val => {
        setFieldValue(field.name, val);
      }}
    />
  );
};

用法(请参阅 Dani 对完整表格声明的回答):

...
<DatePickerField name="date" />
...

代码沙盒中的代码

这是现在的最佳答案(截至 2019 年底)
2021-05-25 00:49:58
还有一个直接的setValue帮手。const [field,, { setValue }] = useField(props);接着onChange={val => setValue(val)}
2021-06-08 00:49:58
一开始让我困惑的一件事 - 使用这个,你不需要添加onChangevalue 道具,你不需要它们,它可以在没有它们的情况下工作。
2021-06-08 00:49:58
我发现 Formik/Yup 和此解决方案存在问题。任何非必需验证都需要重新触摸要传播的日期选择器,而必需验证会立即触发。这可能是一个是的问题。我为此创建了一个线程:stackoverflow.com/questions/67662804/...
2021-06-12 00:49:58
谢谢不用担心,我想出了我的问题。我指的是像 React-Bootstrap 或 Material-UI 这样的库,在 React-Datapicker 上设置is-invalidorerror类,类似于其他控件,以启用常见的样式和错误消息。为了支持这一点,我们只需要将isInvalidor属性传递给这个包装器error,并手动设置 className,例如className={"form-control " + props.isInvalid ? "is-invalid" : ""}. 换句话说,要使日期选择器在错误样式方面适合那些 React 组件库,需要做更多的手动工作。
2021-06-15 00:49:58

react- datepicker 可以通过使用setFieldValueFormik 一起使用

const DatePickerField = ({ name, value, onChange }) => {
    return (
        <DatePicker
            selected={(value && new Date(value)) || null}
            onChange={val => {
                onChange(name, val);
            }}
        />
    );
};

const App = () => (
    <Formik
        initialValues={{ date: "" }}
        ...
    >
        {props => {
            const {
                values,
                handleSubmit,
                setFieldValue
                ...
            } = props;
            return (
                <form onSubmit={handleSubmit}>
                    <DatePickerField
                        name="date"
                        value={values.date}
                        onChange={setFieldValue}
                    />
                    ...

CodeSandbox 演示在这里

@DamienRomito - 我的猜测:如果你DatePicker直接使用,那就是你需要做的。此答案将该逻辑移至DatePickerField组件中,该组件是DatePicker. 在那里,它可以使用name参数,因此不必对 name 进行硬编码'date'
2021-05-27 00:49:58
我不知道为什么我必须这样做: onChange={(val) => setFieldValue('date', val)}
2021-05-31 00:49:58
@DaniVijay - 使用 Formik v2 的useFormikContextand useField,可以简化 DatePickerField 的使用,因此不需要在每次调用时显式传入value传入onChange更新了演示用法:<DatePicker name="date" />我的回答
2021-06-16 00:49:58

我从您的代码中看到的是 DatePicker 不在 Formik 中。在 React.js 中,我们总是创建可重用的组件,以保持代码干净且易于测试。为了能够在您的代码中使用 DatePicker,请创建一个单独的文件,为 Datepicker 编写代码并将其呈现为 Field 的一个组件。这是您应该如何实施。

//First let's start with structure of Formik.
import PortDate from "./form/PortDate";
//define your components in a different directory. I named it form
const CreateForm = props => (
    <div>
      <Formik
        initialValues={{"your initial values"}}
        validate={your validation function here}
        onSubmit={values => {
          return props.onSubmit(values);
        }}
      >
        {({ isSubmitting }) => (
          <Form>     
            <Field name="startDate" component={DatePicker} label="Start Date" />

            <Field
              name="endDate"
              component={DatePicker}
              label="End Date"
              canBeDisabled={true}
            />

          </Form>
        )}
      </Formik>
    </div>
  );

现在在不同的组件中实现我们的 Datepicker 逻辑。//当你想添加样式时使用 reactstrap 而不是 bootstrap

import { FormGroup, Label } from "reactstrap";
class DatePicker extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        dueDate: new Date(),
      };
      this.handleChange = this.handleChange.bind(this);
    }
    setFieldValueAndTouched(date, touched) {
      const { setFieldValue, setFieldTouched } = this.props.form;
      const { name } = this.props.field;
      setFieldValue(name, date, true); //field,value,shouldValidate
      setFieldTouched(name, touched, true); //field,touched,shouldValidate
    }
    handleChange(date) {

      this.setState(() => ({ dateValue: date }));
      this.setFieldValueAndTouched(date, true);    }

    render() {
      const { dueDate } = this.state;
      const {
        label,
        field,
        form: { touched, errors },
      } = this.props;
      // field and form props are passed to this component automatically because we render this inside component of the Field. 
      return (
        <FormGroup>
          <Label>{label}</Label>
          <div className="input-group">

              <DatePicker

                selected={dueDate}
                onChange={this.handleChange}
                peekNextMonth
                showMonthDropdown
                showYearDropdown
                maxDate={new Date()}
                dropdownMode="select"
              />

          </div>
        </FormGroup>
      );
    }
  }

  export default DatePicker;
问题是关于使用在导入的module中定义的现有 DatePicker 类问题中没有任何代码可以移动到不同的文件;它只是一个包含两个<DatePicker>元素的表单抱歉,我认为您的回答与此问题无关。
2021-05-28 00:49:58

如何使其适用于日期范围选择器:

const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const { setFieldValue } = useFormikContext();
const [field] = useField({ name: name, value: startDate });
const [field2] = useField({ name: name2, value: endDate });

<GroupContainer>
 <DatePicker
    {...field}
    onFocus={() => setFocusStart(true)}
    onCalendarClose={() => setFocusStart(false)}
    selected={(field.value && new Date(field.value)) || null}
    onChange={(val) => {
                    setStartDate(val);
                    setFieldValue(field.name, val);
                }}
    dateFormat="dd.MM.yyyy" 
    selectsStart                            
    minDate={new Date()}                                
            />
</GroupContainer>

<GroupContainer>
  <DatePicker
    {...field2}
    onFocus={() => setFocusEnd(true)}
    onCalendarClose={() => setFocusEnd(false)}          
    selected={(field2.value && new Date(field2.value)) || null}
    onChange={(val) => {
            setEndDate(val);
            setFieldValue(field2.name, val);
                }}
    dateFormat="dd.MM.yyyy"         
    selectsEnd
    minDate={new Date()}        
            />
</GroupContainer>

然后:

<ReactDatePicker name="arrivalDate" name2="departureDate" />

更新Dani Vijay 和 ToolmakerSteve 的回答

setValue直接使用而不是setFieldValue. 基于@asologor 的评论

import React from "react";
import { useField } from "formik";
import DatePicker from "react-datepicker";

export const DatePickerField = ({ ...props }) => {
  const [field, , { setValue }] = useField(props);
  return (
    <DatePicker
      {...field}
      {...props}
      selected={(field.value && new Date(field.value)) || null}
      onChange={(val) => {
        setValue(val);
      }}
    />
  );
};

注意:此方法允许您将 Formik 与 一起使用react-datepicker,但它也适用于其他 UI 库,例如Material-UIAnt Design