Formik + yup 表单校验
MyForm.js:
import React, { useEffect, useState } from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import Alert from 'react-bootstrap/Alert';
import PropTypes from 'prop-types';
import { Formik } from 'formik';
import moment from 'moment';
import Api from '../../../api';
import { getValidationSchema, getFormFields } from './config';
function MyForm(props) {
const [countrySelectOptions, setCountrySelectOptions] = useState([]);
const [slaSelectOptions, setSlaSelectOptions] = useState([]);
const [currencySelectOptions, setCurrencySelectOptions] = useState([]);
const [tierSelectOptions, setTierSelectOptions] = useState([]);
const [showSuccess, setShowSuccess] = useState(false);
const [showError, setShowError] = useState(false);
const [initialValues, setInitialValues] = useState({});
const [isCountryLoading, setIsCountryLoading] = useState(true);
const [isTierLoading, setIsTierLoading] = useState(true);
const { addPriceMaster, clearErrors, data, user, addSuccess, addError } =
props;
let addInitialValues = {};
let isTest = false;
if (isTest) {
addInitialValues = {
usersCountry: 'US',
serviceLevelAgreement: 'Next Business Day',
costStopDate: moment(Date.now()).format('YYYY-MM-DD'),
usersCurrency: 'USD',
priceStartDate: moment(Date.now()).format('YYYY-MM-DD'),
comments: '1',
chargeTypeName: '1',
priceStopDate: moment(Date.now()).format('YYYY-MM-DD'),
price: 1.1,
tier: 1,
costStartDate: moment(Date.now()).format('YYYY-MM-DD'),
cost: 1.1,
};
} else {
addInitialValues = {
usersCountry: '',
serviceLevelAgreement: '',
costStopDate: '',
usersCurrency: '',
priceStartDate: '',
comments: '',
chargeTypeName: '',
priceStopDate: '',
price: '',
tier: '',
costStartDate: '',
cost: '',
};
}
const defaultCountry = 'US';
// set form field
const handleFieldChange = (e, setFieldValue, field, type = 'text') => {
let value;
if (type === 'text') {
value = e.target.value;
} else if (type === 'date') {
value = e.currentTarget.value;
} else if (type === 'select') {
value = e.value;
} else if (type === 'dateForCountry') {
value = e;
}
setFieldValue(field, value);
if (field === 'usersCountry') {
const tempCountry = data.find((item) => item.countryCode === e.value);
if (tempCountry) {
setFieldValue('usersCurrency', tempCountry.currencyISO);
}
}
};
// submit
const handleSubmit = (values, formik) => {
const { setSubmitting, resetForm } = formik;
setSubmitting(true);
const callback = () => {
resetForm();
};
let tempValues = { ...values };
tempValues.serviceChargeType = tempValues.chargeTypeName;
delete tempValues.chargeTypeName;
tempValues.costStopDate = moment(tempValues.costStopDate).format(
'MM/DD/YYYY'
);
tempValues.priceStartDate = moment(tempValues.priceStartDate).format(
'MM/DD/YYYY'
);
tempValues.priceStopDate = moment(tempValues.priceStopDate).format(
'MM/DD/YYYY'
);
tempValues.costStartDate = moment(tempValues.costStartDate).format(
'MM/DD/YYYY'
);
addPriceMaster(tempValues, user, callback);
};
// close alert
const handleCloseAlert = (event, resetForm) => {
resetForm();
setShowSuccess(false);
setShowError(false);
clearErrors();
};
// country data, Currency data
useEffect(() => {
const formatCountryData = (data) => {
const options = data.map((country) => {
const { countryCode } = country;
return {
value: countryCode,
label: countryCode,
};
});
setCountrySelectOptions(options);
};
formatCountryData(data);
if (Array.isArray(data) && data.length > 0) {
setIsCountryLoading(false);
} else {
setIsCountryLoading(true);
}
const formatCurrencyData = (data) => {
const options = data.map((country) => {
const { value, label } = country;
return {
value,
label,
};
});
setCurrencySelectOptions(options);
};
let currencyOptionArr = data.map((item) => {
return {
value: item.currencyISO,
label: item.currencyISO,
};
});
formatCurrencyData(currencyOptionArr);
}, [data]);
//SLA data, Tier data
useEffect(() => {
const formatTierData = (data) => {
const options = data.map((country) => {
const { value, label } = country;
return {
value,
label,
};
});
setTierSelectOptions(options);
};
Api.light.priceMasterTierList().then((res) => {
if (Array.isArray(res.data.content)) {
let tierOptionArr = [];
tierOptionArr = res.data.content.map((item) => item.serviceTierCode);
tierOptionArr = [...new Set(tierOptionArr)];
tierOptionArr = tierOptionArr.map((item) => {
return {
value: item,
label: item,
};
});
formatTierData(tierOptionArr);
setIsTierLoading(false);
}
});
const formatSlaData = (data) => {
const options = data.map((country) => {
const { value, label } = country;
return {
value,
label,
};
});
setSlaSelectOptions(options);
};
let slaOptionArr = [
{ label: 'Next Business Day', value: 'Next Business Day' },
{ label: 'Same Business Day', value: 'sameBusinessDay' },
{ label: 'Depot CCI delivery', value: 'Depot/CCI delivery' },
{ label: 'ADP', value: 'ADP' },
{ label: 'ADP ONE', value: 'ADP ONE' },
{ label: 'KYD', value: 'KYD' },
{ label: 'SBTY', value: 'sybt' },
{ label: 'Premier Support 24x7 365', value: 'premiere365' },
{ label: 'Premium Care', value: 'premiumCare' },
];
formatSlaData(slaOptionArr);
}, []);
// success
useEffect(() => {
if (addSuccess.isSuccess) {
setShowSuccess(true);
const timer = setTimeout(() => {
setShowSuccess(false);
clearErrors();
clearTimeout(timer);
}, 2000);
}
}, [addSuccess.isSuccess]);
// error
useEffect(() => {
if (addError.isError) {
setShowError(true);
const timer = setTimeout(() => {
setShowError(false);
clearErrors();
clearTimeout(timer);
}, 2000);
}
}, [addError.isError]);
return (
<Formik
initialValues={{
...initialValues,
...addInitialValues,
}}
validationSchema={getValidationSchema()}
onSubmit={handleSubmit}
>
{(formik) => {
//console.log(666, formik.values, formik.touched, formik.errors)
const { resetForm, isSubmitting } = formik;
return (
<Form noValidate onSubmit={formik.handleSubmit}>
{getFormFields({
formik,
defaultCountry,
countrySelectOptions,
slaSelectOptions,
currencySelectOptions,
tierSelectOptions,
isCountryLoading,
isTierLoading,
handleFieldChange,
})}
<Button
type="submit"
className="table-button-right "
disabled={isSubmitting}
>
ADD
</Button>
<Alert
variant="success"
dismissible={true}
show={showSuccess}
onClose={(event) => handleCloseAlert(event, resetForm)}
role="alert"
className="user-form-alert"
>
<strong>Success: </strong>
{addSuccess.msg}
</Alert>
<Alert
variant="danger"
dismissible={true}
show={showError}
onClose={(event) => handleCloseAlert(event, resetForm)}
role="alert"
className="user-form-alert"
>
<strong>Failure: </strong> {addError.msg}
</Alert>
</Form>
);
}}
</Formik>
);
}
MyForm.propTypes = {
user: PropTypes.object,
addPriceMaster: PropTypes.func.isRequired,
data: PropTypes.array,
getCountriesByPage: PropTypes.func,
addError: PropTypes.object,
addSuccess: PropTypes.object,
clearErrors: PropTypes.func,
};
export default MyForm;
config.js:
import React from 'react';
import * as yup from 'yup';
import Form from 'react-bootstrap/Form';
import { Col } from 'react-bootstrap';
import InternationalCalendarField from '../../../components/Fields/InternationalDateField';
import CustomSelect from '../../../components/Select/CustomSelect';
import moment from 'moment';
import {
dateTestAfterToday,
dateTestAfterOtherDay,
dateTestBeforeOtherDay,
greaterThanZero,
} from '../../../utils/tools';
// Form verification field
const getValidationSchema = () => {
const requireMsg = 'This is a required field';
const dateTestAfterTodayMsg = 'Please select a date after today';
const dateTestAfterOtherDayMsg = 'Please select a date after the start date';
const dateTestBeforeOtherDayMsg = 'Please select a date before the end date';
const validationSchema = yup.object({
usersCountry: yup.string().trim().required(requireMsg),
serviceLevelAgreement: yup.string().trim().required(requireMsg),
costStopDate: yup
.string()
.trim()
.required(requireMsg)
.test('costStopDate', dateTestAfterTodayMsg, dateTestAfterToday)
.test(
'costStopDate',
dateTestAfterOtherDayMsg,
dateTestAfterOtherDay('costStartDate')
),
usersCurrency: yup.string().trim().required(requireMsg),
priceStartDate: yup.string().trim().required(requireMsg).test(
'priceStartDate',
dateTestBeforeOtherDayMsg,
dateTestBeforeOtherDay('priceStopDate')
),
comments: yup.string().required(requireMsg),
chargeTypeName: yup.string().trim().required(requireMsg),
priceStopDate: yup
.string()
.trim()
.required(requireMsg)
.test('priceStopDate', dateTestAfterTodayMsg, dateTestAfterToday)
.test(
'priceStopDate',
dateTestAfterOtherDayMsg,
dateTestAfterOtherDay('priceStartDate')
),
price: yup.number().required(requireMsg).test('price', ' Please enter a number greater than 0', greaterThanZero),
tier: yup.string().trim().required(requireMsg),
costStartDate: yup
.string()
.trim()
.required(requireMsg)
.test(
'costStartDate',
dateTestBeforeOtherDayMsg,
dateTestBeforeOtherDay('costStopDate')
),
cost: yup.number().required(requireMsg).test('price', ' Please enter a number greater than 0', greaterThanZero),
});
return validationSchema;
};
// form field
const getFormFields = (props) => {
const {
formik,
defaultCountry,
countrySelectOptions,
slaSelectOptions,
currencySelectOptions,
tierSelectOptions,
isCountryLoading,
isTierLoading,
handleFieldChange,
} = props;
const { setFieldValue, resetForm, values, touched, errors, isSubmitting } =
formik;
return (
<>
<Form.Row>
<Form.Group as={Col} md={4} controlId="usersCountry">
<Form.Label className="required">Country</Form.Label>
<Form.Control
required
as={CustomSelect}
isLoading={isCountryLoading}
id="usersCountry"
{...formik.getFieldProps('usersCountry')}
options={countrySelectOptions}
onChange={(value) =>
handleFieldChange(value, setFieldValue, 'usersCountry', 'select')
}
isInvalid={touched.usersCurrency && errors.usersCountry}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{values.usersCurrency
? ''
: touched.usersCurrency && errors.usersCountry}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md={4} controlId="serviceLevelAgreement">
<Form.Label className="required">SLA</Form.Label>
<Form.Control
required
as={CustomSelect}
id="serviceLevelAgreement"
{...formik.getFieldProps('serviceLevelAgreement')}
onChange={(value) =>
handleFieldChange(
value,
setFieldValue,
'serviceLevelAgreement',
'select'
)
}
options={slaSelectOptions}
isInvalid={
touched.serviceLevelAgreement && errors.serviceLevelAgreement
}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.serviceLevelAgreement && errors.serviceLevelAgreement}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md={4} controlId="costStopDate">
<Form.Label className="required">Cost Stop Date</Form.Label>
<Form.Control
required
as={InternationalCalendarField}
id="costStopDate"
{...formik.getFieldProps('costStopDate')}
country={values.usersCountry ? values.usersCountry : defaultCountry}
date={values.costStopDate}
onCalendarChange={(value) => {
handleFieldChange(
value,
setFieldValue,
'costStopDate',
'dateForCountry'
);
}}
storedDateFormat="yyyy-MM-dd"
errorMsg={false}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{(touched.costStopDate || values.costStopDate) && errors.costStopDate}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId="usersCurrency">
<Form.Label className="required">Currency</Form.Label>
<Form.Control
required
as={CustomSelect}
isLoading={isCountryLoading}
value={values.usersCurrency}
isDisabled={true}
options={currencySelectOptions}
id="usersCurrency"
{...formik.getFieldProps('usersCurrency')}
onChange={(value) =>
handleFieldChange(value, setFieldValue, 'usersCurrency', 'select')
}
isInvalid={touched.usersCurrency && errors.usersCurrency}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.usersCurrency && errors.usersCurrency}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} controlId="priceStartDate">
<Form.Label className="required">Price Start Date</Form.Label>
<Form.Control
required
placeholder="Select Date"
as={InternationalCalendarField}
id="priceStartDate"
{...formik.getFieldProps('priceStartDate')}
country={values.usersCountry ? values.usersCountry : defaultCountry}
date={values.priceStartDate}
onCalendarChange={(value) => {
handleFieldChange(
value,
setFieldValue,
'priceStartDate',
'dateForCountry'
);
}}
storedDateFormat="yyyy-MM-dd"
errorMsg={false}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{(touched.priceStartDate || values.priceStartDate) && errors.priceStartDate}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} controlId="comments">
<Form.Label className="required">Comments</Form.Label>
<Form.Control
required
id="comments"
type="text"
{...formik.getFieldProps('comments')}
placeholder="Enter"
isInvalid={touched.comments && errors.comments}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.comments && errors.comments}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId="chargeTypeName">
<Form.Label className="required">Charge Type Name</Form.Label>
<Form.Control
required
id="chargeTypeName"
{...formik.getFieldProps('chargeTypeName')}
type="text"
placeholder="Enter"
isInvalid={touched.chargeTypeName && errors.chargeTypeName}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.chargeTypeName && errors.chargeTypeName}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} controlId="priceStopDate">
<Form.Label className="required">Price Stop Date</Form.Label>
<Form.Control
required
as={InternationalCalendarField}
country={values.usersCountry ? values.usersCountry : defaultCountry}
date={values.priceStopDate}
onCalendarChange={(value) => {
handleFieldChange(
value,
setFieldValue,
'priceStopDate',
'dateForCountry'
);
}}
id="priceStopDate"
{...formik.getFieldProps('priceStopDate')}
errorMsg={false}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{(touched.priceStopDate || values.priceStopDate) && errors.priceStopDate}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} controlId="price">
<Form.Label className="required">Price</Form.Label>
<Form.Control
required
type="number"
id="price"
{...formik.getFieldProps('price')}
placeholder="Enter"
isInvalid={touched.price && errors.price}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.price && errors.price}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} controlId="tier">
<Form.Label className="required">Tier</Form.Label>
<Form.Control
required
as={CustomSelect}
isLoading={isTierLoading}
options={tierSelectOptions}
id="tier"
{...formik.getFieldProps('tier')}
onChange={(value) =>
handleFieldChange(value, setFieldValue, 'tier', 'select')
}
isInvalid={touched.tier && errors.tier}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.tier && errors.tier}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} controlId="costStartDate">
<Form.Label className="required">Cost Start Date</Form.Label>
<Form.Control
required
type="date"
as={InternationalCalendarField}
country={values.usersCountry ? values.usersCountry : defaultCountry}
date={values.costStartDate}
onCalendarChange={(value) => {
handleFieldChange(
value,
setFieldValue,
'costStartDate',
'dateForCountry'
);
}}
id="costStartDate"
{...formik.getFieldProps('costStartDate')}
errorMsg={false}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{(touched.costStartDate || values.costStartDate) && errors.costStartDate}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} controlId="cost">
<Form.Label className="required">Cost</Form.Label>
<Form.Control
required
type="number"
placeholder="Enter"
isInvalid={touched.cost && errors.cost}
id="cost"
{...formik.getFieldProps('cost')}
/>
<Form.Control.Feedback
className="select-invalid-feedback"
type="invalid"
>
{touched.cost && errors.cost}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
</>
);
};
// Table column
const getColumns = () => {
return [
{
dataField: 'id',
text: 'ID',
hidden: true,
},
{
dataField: 'usersCountry',
text: 'Country',
},
{
dataField: 'usersCurrency',
text: 'Currency',
},
{
dataField: 'serviceChargeType',
text: 'Charge Type Name',
},
{
dataField: 'tier',
text: 'Tier',
},
{
dataField: 'serviceLevelAgreement',
text: 'SLA',
},
{
dataField: 'priceStartDate',
text: 'Price Start Date',
formatter: (text) => {
return moment(text).format('MM/DD/YYYY');
},
},
{
dataField: 'priceStopDate',
text: 'Price Stop Date',
formatter: (text) => {
return moment(text).format('MM/DD/YYYY');
},
},
{
dataField: 'costStartDate',
text: 'Cost Start Date',
formatter: (text) => {
return moment(text).format('MM/DD/YYYY');
},
},
{
dataField: 'costStopDate',
text: 'Cost Stop Date',
formatter: (text) => {
return moment(text).format('MM/DD/YYYY');
},
},
{
dataField: 'comments',
text: 'Comments',
},
{
dataField: 'price',
text: 'Price',
},
{
dataField: 'cost',
text: 'Cost',
},
];
};
export { getValidationSchema, getFormFields, getColumns };
tools.js:
import moment from 'moment'
//get userinfo
const getUserInfo = () => {
const tempLocalStorageUserInfo = localStorage.getItem('moto')
const { access_token, token_type } = tempLocalStorageUserInfo
? JSON.parse(tempLocalStorageUserInfo)
: {}
return {
token: `${token_type} ${access_token}`
}
}
//after today
const dateTestAfterToday = function (value) {
const now = moment(Date.now()).format('YYYY-MM-DD')
if (moment(value).valueOf() <= moment(now).valueOf()) {
return false
} else {
return true
}
}
//after other day
const dateTestAfterOtherDay = function (field) {
return function (value, context) {
if (context.parent[field] && moment(value).valueOf() <= moment(context.parent[field]).valueOf()) {
return false
} else {
return true
}
}
}
//before other day
const dateTestBeforeOtherDay = function (field) {
return function (value, context) {
if (context.parent[field] && moment(value).valueOf() >= moment(context.parent[field]).valueOf()) {
return false
} else {
return true
}
}
}
// Greater than 0
const greaterThanZero = (value) => {
return value > 0
}
export {
getUserInfo,
dateTestAfterToday,
dateTestAfterOtherDay,
dateTestBeforeOtherDay,
greaterThanZero,
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步