【问题标题】:Ant design date and time pickers do not pass value through Formik (react)Ant 设计日期和时间选择器不通过 Formik 传递值(反应)
【发布时间】:2019-04-20 15:47:40
【问题描述】:

我目前正在使用 Formik 在 React 中处理预订表单。我还分别为预订日期和时间合并了 Ant Design 的 Date Picker 和 Time Picker,但是我很难将值传递回组件。

这是我在表单组件中的设置方式(我省略了其他不相关的字段):

const { booking, handleSubmit, mode } = this.props;

...

<Formik
    initialValues={booking}
    onSubmit={handleSubmit}
    render={({errors, touched, isSubmitting}) => (
        <Form>
        ...
<div className="form-group col-sm-4 col-md-6 col-lg-4">
    <label htmlFor="booking_date">
        Booking Date <span className="required">*</span>
    </label>
    <DatePicker onChange={ (date, dateString) => setFieldValue('booking_date', dateString)} defaultValue={this.state.bookingDate}
        className="form-control" format={this.state.dateFormat} />
</div>
<div className="form-group col-sm-4 col-md-6 col-lg-4">
    <label htmlFor="start_time">
        Start Time <span className="required">*</span>
    </label>
    <TimePicker
        defaultValue={this.state.startTime}
        format={this.state.timeFormat}
        className="form-control"
        onChange={this.handleStartTimeChange}
        minuteStep={5}
        id="start_time"
        name="start_time"
    />
</div>

这是处理时间变化的函数(只是一个状态集):

handleStartTimeChange(time) {
    this.setState({
        startTime: time
    });
}

然后在父节点上,组件是这样设置的:

<BookingForm
    show={true}
    booking={null}
    handleSubmit={this.saveBooking.bind(this)}
    mode="add"
/>

saveBooking 函数只是简单地控制台记录参数。但是,它只会注销其他字段,例如 firstnamesurnameemail。日期完全被忽略了,我不知道如何让表单识别它们——我什至尝试创建一个 Formik 隐藏字段来在提交时复制日期值,但它仍然忽略它。字段名称和 ID 是正确的,并且与所有其他字段一样与数据库相关 - 所以我不明白为什么它不会读取该数据?

【问题讨论】:

    标签: reactjs datepicker formik ant-design-pro


    【解决方案1】:

    简单地说,您需要在 Formik Fieldcomponent 属性中使用 Ant Design 的 Form.Item

    您也可以添加其他 Antd 表单项,但是有一些怪癖。因此,我只建议使用其中一种(不要同时使用)。

    工作示例https://codesandbox.io/s/4x47oznvvx

    components/AntFields.js(创建两个不同的onChange 函数的原因是因为其中一个ant 组件传回event (event.target.value) 而另一个传回一个value -- 不幸的是,将FormikAntd 一起使用时的怪癖)

    import map from "lodash/map";
    import React from "react";
    import { DatePicker, Form, Input, TimePicker, Select } from "antd";
    
    const FormItem = Form.Item;
    const { Option } = Select;
    
    const CreateAntField = Component => ({
      field,
      form,
      hasFeedback,
      label,
      selectOptions,
      submitCount,
      type,
      ...props
    }) => {
      const touched = form.touched[field.name];
      const submitted = submitCount > 0;
      const hasError = form.errors[field.name];
      const submittedError = hasError && submitted;
      const touchedError = hasError && touched;
      const onInputChange = ({ target: { value } }) =>
        form.setFieldValue(field.name, value);
      const onChange = value => form.setFieldValue(field.name, value);
      const onBlur = () => form.setFieldTouched(field.name, true);
      return (
        <div className="field-container">
          <FormItem
            label={label}
            hasFeedback={
              (hasFeedback && submitted) || (hasFeedback && touched) ? true : false
            }
            help={submittedError || touchedError ? hasError : false}
            validateStatus={submittedError || touchedError ? "error" : "success"}
          >
            <Component
              {...field}
              {...props}
              onBlur={onBlur}
              onChange={type ? onInputChange : onChange}
            >
              {selectOptions &&
                map(selectOptions, name => <Option key={name}>{name}</Option>)}
            </Component>
          </FormItem>
        </div>
      );
    };
    
    export const AntSelect = CreateAntField(Select);
    export const AntDatePicker = CreateAntField(DatePicker);
    export const AntInput = CreateAntField(Input);
    export const AntTimePicker = CreateAntField(TimePicker);
    

    components/FieldFormats.js

    export const dateFormat = "MM-DD-YYYY";
    export const timeFormat = "HH:mm";
    

    components/ValidateFields.js

    import moment from "moment";
    import { dateFormat } from "./FieldFormats";
    
    export const validateDate = value => {
      let errors;
    
      if (!value) {
        errors = "Required!";
      } else if (
        moment(value).format(dateFormat) < moment(Date.now()).format(dateFormat)
      ) {
        errors = "Invalid date!";
      }
    
      return errors;
    };
    
    export const validateEmail = value => {
      let errors;
    
      if (!value) {
        errors = "Required!";
      } else if (!/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(value)) {
        errors = "Invalid email address!";
      }
    
      return errors;
    };
    
    export const isRequired = value => (!value ? "Required!" : "");
    

    组件/RenderBookingForm.js

    import React from "react";
    import { Form, Field } from "formik";
    import { AntDatePicker, AntInput, AntSelect, AntTimePicker } from "./AntFields";
    import { dateFormat, timeFormat } from "./FieldFormats";
    import { validateDate, validateEmail, isRequired } from "./ValidateFields";
    
    export default ({ handleSubmit, values, submitCount }) => (
      <Form className="form-container" onSubmit={handleSubmit}>
        <Field
          component={AntInput}
          name="email"
          type="email"
          label="Email"
          validate={validateEmail}
          submitCount={submitCount}
          hasFeedback
        />
        <Field
          component={AntDatePicker}
          name="bookingDate"
          label="Booking Date"
          defaultValue={values.bookingDate}
          format={dateFormat}
          validate={validateDate}
          submitCount={submitCount}
          hasFeedback
        />
        <Field
          component={AntTimePicker}
          name="bookingTime"
          label="Booking Time"
          defaultValue={values.bookingTime}
          format={timeFormat}
          hourStep={1}
          minuteStep={5}
          validate={isRequired}
          submitCount={submitCount}
          hasFeedback
        />
        <Field
          component={AntSelect}
          name="bookingClient"
          label="Client"
          defaultValue={values.bookingClient}
          selectOptions={values.selectOptions}
          validate={isRequired}
          submitCount={submitCount}
          tokenSeparators={[","]}
          style={{ width: 200 }}
          hasFeedback
        />
        <div className="submit-container">
          <button className="ant-btn ant-btn-primary" type="submit">
            Submit
          </button>
        </div>
      </Form>
    );
    

    components/BookingForm.js

    import React, { PureComponent } from "react";
    import { Formik } from "formik";
    import RenderBookingForm from "./RenderBookingForm";
    import { dateFormat, timeFormat } from "./FieldFormats";
    import moment from "moment";
    
    const initialValues = {
      bookingClient: "",
      bookingDate: moment(Date.now()),
      bookingTime: moment(Date.now()),
      selectOptions: ["Mark", "Bob", "Anthony"]
    };
    
    const handleSubmit = formProps => {
      const { bookingClient, bookingDate, bookingTime, email } = formProps;
      const selectedDate = moment(bookingDate).format(dateFormat);
      const selectedTime = moment(bookingTime).format(timeFormat);
      alert(
        `Email: ${email} \nSelected Date: ${selectedDate} \nSelected Time: ${selectedTime}\nSelected Client: ${bookingClient}`
      );
    };
    
    export default () => (
      <Formik
        initialValues={initialValues}
        onSubmit={handleSubmit}
        render={RenderBookingForm}
      />
    );
    

    【讨论】:

    • 我知道这已在不久前得到回答并且确实有效 - 但我有一个问题。我需要将数据传递给呈现的表单(即从选择框中选择的客户端列表),但由于 RenderBookingForm 只是一个导出的常量,我如何将这些数据作为道具传递,然后在RenderBookingForm 组件?这可能吗?
    • 请参阅上面的更新答案(codesandbox 也已更新)。请注意,您必须想出一个策略来创建selectOptions。无论是通过使BookingForm 成为stateful component,它在从API 的componentDidMount 方法中获取客户端列表时,有条件地呈现(reactjs.org/docs/conditional-rendering.html)一个微调器。然后将此 API 客户端列表设置为 state,然后将其传递给表单。或者,通过将selectOptions 设置为字符串的静态数组(如上所示)。无论哪种方式,它的结构都需要:[ "opt1", "opt2" ... ]
    • 啊,我没想到将它传递给 initialValues!是的,我将从 API 调用中检索客户端,所以我只需要转换为数组而不是我通常会得到的对象,谢谢!
    【解决方案2】:

    我不明白为什么它不会读取该数据?

    Formik 将值作为values 属性传递,它们使用setFieldValue 更新。 当您在状态中存储值时,Formik 对此一无所知

    当然,将值存储到状态并没有错(假设它有效),但是您必须定义内部提交处理程序才能将这些值附加到其他状态。通过简单的调用 prop:

    onSubmit={handleSubmit}
    

    你没有机会这样做。只有 Formik 处理的值会被传递。您需要定义内部提交处理程序,例如:

    const handleSubmit = values => {
      // init with other Formik fields
      let preparedValues = { ...values }; 
    
      // values from state
      const { startTime, startDate } = this.state; 
    
      // attach directly or format with moment
      preparedValues["startTime"] = startTime;
      preparedValues["startDate"] = startDate;
    
      // of course w/o formatting it can be done shorter
      // let preparedValues = { ...values, ...this.state }; 
    
      console.log(preparedValues);
    
      // call external handler with all values
      this.prop.handleSubmit( preparedValues );
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-27
      • 1970-01-01
      • 1970-01-01
      • 2011-08-14
      • 2015-05-12
      • 1970-01-01
      • 2018-12-15
      • 2017-12-31
      相关资源
      最近更新 更多