基于Formik、Yup、MUI的注册表单无法提交问题排查
问题原因及修复方案
点击注册按钮无响应的核心原因是用户类型(userType)的下拉框未正确与Formik绑定,导致Yup验证始终不通过,Formik的提交逻辑根本没触发。
具体问题点
userType字段在验证规则里是必填项,但对应的下拉TextField缺失了以下关键属性:
- 未设置
name="userType",Formik无法识别该字段 - 未绑定
handleChange和handleBlur,字段值无法同步到Formik的values中 - 未关联
values.userType作为当前值 - 未处理验证错误的展示逻辑
修复步骤
修改用户类型选择的TextField,补充Formik相关绑定:
- 添加
name="userType" - 绑定
onChange={handleChange}、onBlur={handleBlur} - 设置
value={values.userType} - 增加错误提示相关的
error和helperText属性
修复后的完整代码
import { Box, Button, MenuItem, TextField, Typography, useMediaQuery, useTheme, } from '@mui/material' import { Formik } from 'formik' import { useNavigate } from 'react-router-dom' import * as yup from 'yup' const registerSchema = yup.object().shape({ firstName: yup.string().required('required'), lastName: yup.string().required('required'), userType: yup.string().required('required'), userName: yup.string().required('required'), email: yup.string().email('invalid email').required('required'), password: yup.string().required('required'), confirmPassword: yup.string().required('required'), }) const initialValuesRegister = { firstName: '', lastName: '', userType: '', userName: '', email: '', password: '', confirmPassword: '', } const type = [ { value: 'Student', label: 'Student', }, { value: 'Teacher', label: 'Teacher', }, ] const FormRegister = () => { const { palette } = useTheme() const navigate = useNavigate() const isNonMobile = useMediaQuery('(min-width:600px)') const register = async (values, onSubmitProps) => { const savedUserResponse = await fetch( 'http://localhost:3001/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(values), } ) const savedUser = await savedUserResponse.json() onSubmitProps.resetForm() if (savedUser) { console.log(savedUser) navigate('/') } } const handleFormSubmit = async (values, onSubmitProps) => { console.log('Test') console.log(values, onSubmitProps) await register(values, onSubmitProps) } return ( <Formik onSubmit={handleFormSubmit} initialValues={initialValuesRegister} validationSchema={registerSchema} > {({ values, errors, touched, handleBlur, handleChange, handleSubmit, resetForm, }) => ( <form onSubmit={handleSubmit}> <Box display="grid" gap="30px" gridTemplateColumns="repeat(4, minmax(0, 1fr))" sx={{ '& > div': { gridColumn: isNonMobile ? undefined : 'span 4' }, }} > <TextField label="First Name" onBlur={handleBlur} onChange={handleChange} value={values.firstName} name="firstName" error={Boolean(touched.firstName) && Boolean(errors.firstName)} helperText={touched.firstName && errors.firstName} sx={{ gridColumn: 'span 2' }} /> <TextField label="Last Name" onBlur={handleBlur} onChange={handleChange} value={values.lastName} name="lastName" error={Boolean(touched.lastName) && Boolean(errors.lastName)} helperText={touched.lastName && errors.lastName} sx={{ gridColumn: 'span 2' }} /> <TextField id="outlined-select" select label="Select Type" name="userType" value={values.userType} onChange={handleChange} onBlur={handleBlur} error={Boolean(touched.userType) && Boolean(errors.userType)} helperText={touched.userType && errors.userType} > {type.map((option) => ( <MenuItem key={option.value} value={option.value}> {option.label} </MenuItem> ))} </TextField> <TextField label="User Name" onBlur={handleBlur} onChange={handleChange} value={values.userName} name="userName" error={Boolean(touched.userName) && Boolean(errors.userName)} helperText={touched.userName && errors.userName} sx={{ gridColumn: 'span 4' }} /> <TextField label="Email" onBlur={handleBlur} onChange={handleChange} value={values.email} name="email" error={Boolean(touched.email) && Boolean(errors.email)} helperText={touched.email && errors.email} sx={{ gridColumn: 'span 4' }} /> <TextField label="Password" type="password" onBlur={handleBlur} onChange={handleChange} value={values.password} name="password" error={Boolean(touched.password) && Boolean(errors.password)} helperText={touched.password && errors.password} sx={{ gridColumn: 'span 4' }} /> <TextField label="Confirm Password" type="password" onBlur={handleBlur} onChange={handleChange} value={values.confirmPassword} name="confirmPassword" error={ Boolean(touched.confirmPassword) && Boolean(errors.confirmPassword) } helperText={touched.confirmPassword && errors.confirmPassword} sx={{ gridColumn: 'span 4' }} /> </Box> {/* BUTTONS */} <Box> <Button fullWidth type="submit" sx={{ m: '2rem 0', p: '1rem', backgroundColor: palette.primary.main, color: palette.background.alt, '&:hover': { color: palette.primary.main }, }} > REGISTER </Button> <Typography onClick={() => { navigate('/') resetForm() }} sx={{ textDecoration: 'underline', color: palette.primary.main, '&:hover': { cursor: 'pointer', color: palette.primary.light, }, }} > Already have an account? Login here </Typography> </Box> </form> )} </Formik> ) } export default FormRegister
额外优化建议
可以给userType设置默认值,避免用户未选择时直接触发必填错误:
// 方式1:修改初始值 const initialValuesRegister = { // ...其他字段 userType: 'Student', } // 方式2:在Yup规则里设置默认值 userType: yup.string().default('Student').required('required'),
内容的提问来源于stack exchange,提问作者Strike
相关产品推荐
相关产品推荐

