使用Formik的useField hook验证Select组件报错如何解决
问题根源
两个核心错误导致你的代码无法运行:
useField是依赖Formik上下文的Hook,你当前将它写在了<Formik>组件的外部作用域,Formik上下文未初始化,所以抛出getFieldPropsundefined的报错。useField返回值结构为[field, meta, helpers],你错误地把第一个返回值field当作了helpers使用,同时传参写法不符合新版Formik规范,也出现了变量未定义的问题。
修复步骤
- 移动
useField的调用位置到<Formik>的回调作用域内,保证能访问到Formik上下文 - 修正
useField的传参和返回值解构,新版Formik直接传入字段名字符串即可,不需要传对象 - 删除重复的赋值逻辑,匹配Yup校验规则传递正确的字段值类型
- 补充错误提示展示逻辑
修复后完整代码
import React from 'react'; import * as Yup from 'yup'; import PropTypes from 'prop-types'; import { Formik, useField } from 'formik'; import { Box, Button, FormHelperText, TextField, makeStyles, Grid } from '@material-ui/core'; import { useHistory } from 'react-router-dom'; import countries from '../../profile/candidate/ProfileCreateView/PersonalInfo/countries'; import Select from 'react-select'; const useStyles = makeStyles(theme => ({})); const StepTwo = ({ className, formValues, handleNextStep, handleChangeOnParent, ...rest }) => { const classes = useStyles(); const onSubmitHandler = async ( values, { resetForm, setErrors, setStatus, setSubmitting } ) => { try { const { country: { text: country }, } = values; handleNextStep(2, { country, }); } catch (err) { console.error(err); setStatus({ success: false }); setErrors({ submit: err.message }); setSubmitting(false); } }; // 抽离Select组件,在内部调用useField const CountrySelect = () => { // 正确写法:传字段名字符串,解构出三个返回值 const [field, meta, helpers] = useField("country"); const { setValue, setTouched } = helpers; return ( <> <Select styles={customStyles} placeholder="Country" name={field.name} value={field.value} options={countries} getOptionLabel={option => option.text} onChange={option => { // Yup要求country是对象,直接传整个option,不要只传option.value setValue(option); setTouched(true); handleChangeOnParent('country', option); }} /> {/* 展示校验错误 */} {meta.touched && meta.error && ( <FormHelperText error>{meta.error}</FormHelperText> )} </> ) } return ( <Formik initialValues={{ country: formValues.country || null, }} validationSchema={Yup.object().shape({ country: Yup.object().required('Country is required'), })} onSubmit={onSubmitHandler} enableReinitialize > {({ handleSubmit, isSubmitting }) => ( <form onSubmit={handleSubmit}> <Grid container spacing={3}> <Grid item xs={12}> {/* 直接使用抽离的CountrySelect组件 */} <CountrySelect /> </Grid> </Grid> {/* 提交按钮可根据需求调整 */} <Box mt={3}> <Button disabled={isSubmitting} fullWidth type="submit" variant="contained" color="primary" > 下一步 </Button> </Box> </form> )} </Formik> ); }; StepTwo.propTypes = { className: PropTypes.string }; export default StepTwo;
额外说明
- 如果你不想单独抽离组件,也可以直接在Formik的回调里调用useField,只要保证调用位置处于Formik组件包裹的内部即可
- 之前你传
useField({name: country})时没给country加引号,会被识别为未定义的变量,要传对象参数需要写useField({name: "country"}),不过新版Formik优先推荐直接传字符串参数的写法 - 你之前的
initialValues里country默认值是空字符串,和Yup要求的对象类型不匹配,已经改成了null,避免初始化时报类型错误
内容的提问来源于stack exchange,提问作者emyy96
相关产品推荐
相关产品推荐

