Ant 设计日期和时间选择器不通过 Formik 传递值(反应)
Ant design date and time pickers do not pass value through Formik (react)
我目前正在使用 Formik 在 React 中制作预订表格。我还分别为预订日期和时间合并了 Ant Design 的日期选择器和时间选择器,但我很难将值传回组件。
以下是我在表单组件中的设置方式(我省略了其他不相关的字段):
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 是正确的,并且与所有其他字段一样与数据库相关联 - 所以我不明白为什么它不读取该数据?
简而言之,您需要在 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+)*(\.\w{2,3})+$/.test(value)) {
errors = "Invalid email address!";
}
return errors;
};
export const isRequired = value => (!value ? "Required!" : "");
components/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}
/>
);
I don't understand why it won't read that data?
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 );
}
我目前正在使用 Formik 在 React 中制作预订表格。我还分别为预订日期和时间合并了 Ant Design 的日期选择器和时间选择器,但我很难将值传回组件。
以下是我在表单组件中的设置方式(我省略了其他不相关的字段):
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 是正确的,并且与所有其他字段一样与数据库相关联 - 所以我不明白为什么它不读取该数据?
简而言之,您需要在 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+)*(\.\w{2,3})+$/.test(value)) {
errors = "Invalid email address!";
}
return errors;
};
export const isRequired = value => (!value ? "Required!" : "");
components/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}
/>
);
I don't understand why it won't read that data?
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 );
}