Ant 设计日期和时间选择器不通过 Formik 传递值(反应)
Posted
技术标签:
【中文标题】Ant 设计日期和时间选择器不通过 Formik 传递值(反应)【英文标题】:Ant design date and time pickers do not pass value through Formik (react) 【发布时间】: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
函数只是简单地控制台记录参数。但是,它只会注销其他字段,例如 firstname
、surname
和 email
。日期完全被忽略了,我不知道如何让表单识别它们——我什至尝试创建一个 Formik 隐藏字段来在提交时复制日期值,但它仍然忽略它。字段名称和 ID 是正确的,并且与所有其他字段一样与数据库相关 - 所以我不明白为什么它不会读取该数据?
【问题讨论】:
【参考方案1】:简单地说,您需要在 Formik Field
的 component
属性中使用 Ant Design 的 Form.Item
。
您也可以添加其他 Antd 表单项,但是有一些怪癖。因此,我只建议使用其中一种(不要同时使用)。
工作示例:https://codesandbox.io/s/4x47oznvvx
components/AntFields.js(创建两个不同的onChange
函数的原因是因为其中一个ant 组件传回event
(event.target.value
) 而另一个传回一个value
-- 不幸的是,将Formik
与Antd
一起使用时的怪癖)
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+)*(\.\w2,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 );
【讨论】:
以上是关于Ant 设计日期和时间选择器不通过 Formik 传递值(反应)的主要内容,如果未能解决你的问题,请参考以下文章