You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Formik+MUI注册表单无法提交及Select组件异常求助

注册表单提交及MUI Select组件问题排查与修复

问题描述

本人是React新手,正在使用Formik、Yup、MUI开发注册表单,表单包含username(文本框)、roles(下拉选择器)、college(下拉选择器)、password(密码框)、confirm password(确认密码框)及提交按钮。目前遇到两个问题:

  1. 表单无法提交,即使仅保留username字段,尝试在控制台打印username也无输出;
  2. MUI的Select组件加入表单后无法正常工作,不清楚如何将其选中值传递到表单提交逻辑。最初以为是Select导致提交问题,但移除Select后表单仍无法提交,Select可能还存在其他错误。

核心错误分析

  • 字段名完全不匹配:Formik初始值定义的是username,但输入框的name属性写为registeremail,导致Formik无法追踪该字段的变化,提交时拿不到对应数据。
  • 冗余状态干扰Formik逻辑:自行定义的formValues状态完全多余,Formik会自动管理表单的所有状态(值、验证、提交状态等),手动绑定自定义状态会导致Formik无法同步数据,验证和提交流程全部失效。
  • Select组件绑定错误:注释中的Select组件同样错误绑定了自定义状态,未使用Formik提供的values对象,且验证规则的字段名需要与控件name严格一致。

修正后的完整代码

import { useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Select from '@mui/material/Select';

// material-ui
import {
    Box,
    Button,
    FormControl,
    FormHelperText,
    Grid,
    Link,
    IconButton,
    InputAdornment,
    InputLabel,
    OutlinedInput,
    Stack,
    Typography
} from '@mui/material';

// third party
import * as Yup from 'yup';
import { Formik } from 'formik';

// project import
import AnimateButton from 'components/@extended/AnimateButton';
import { strengthColor, strengthIndicator } from 'utils/password-strength';

// assets
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';

const AuthRegister = () => {
    const [level, setLevel] = useState();
    const [showPassword, setShowPassword] = useState(false);

    const handleClickShowPassword = () => {
        setShowPassword(!showPassword);
    };

    const handleMouseDownPassword = (event) => {
        event.preventDefault();
    };

    return (
        <>
            <Formik
                initialValues={{
                    username: '',
                    roles: '',
                    colleges: '',
                    password: '',
                    passwordConfirm: ''
                }}
                validationSchema={Yup.object().shape({
                    username: Yup.string().email('必须是有效的邮箱').max(255).required('邮箱为必填项'),
                    roles: Yup.string().required('请选择角色'),
                    colleges: Yup.string().required('请选择学院'),
                    password: Yup.string().max(255).required('密码为必填项'),
                    passwordConfirm: Yup.string()
                        .oneOf([Yup.ref('password')], '两次输入的密码不一致')
                        .required('确认密码为必填项')
                })}
                onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {
                    console.log('进入提交逻辑');
                    try {
                        console.log('提交的表单数据:', JSON.stringify(values, null, 2));
                        // 在这里添加实际的提交逻辑,比如调用API
                        setStatus({ success: true });
                    } catch (err) {
                        console.error('提交失败:', err);
                        setStatus({ success: false });
                        setErrors({ submit: err.message });
                    } finally {
                        setSubmitting(false);
                    }
                }}
            >
                {({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => (
                    <form noValidate onSubmit={handleSubmit}>
                        <Grid container spacing={3}>
                            <Grid item xs={12}>
                                <Stack spacing={1}>
                                    <InputLabel htmlFor="username">用户名(邮箱)*</InputLabel>
                                    <OutlinedInput
                                        id="username"
                                        type="email"
                                        value={values.username}
                                        name="username"
                                        onBlur={handleBlur}
                                        onChange={handleChange}
                                        placeholder="abc@gmail.com"
                                        fullWidth
                                        error={Boolean(touched.username && errors.username)}
                                    />
                                    {touched.username && errors.username && (
                                        <FormHelperText error id="helper-text-username">
                                            {errors.username}
                                        </FormHelperText>
                                    )}
                                </Stack>
                            </Grid>

                            <Grid item xs={12} md={6}>
                                <Stack spacing={1}>
                                    <InputLabel htmlFor="roles">角色*</InputLabel>
                                    <Select
                                        native
                                        onChange={handleChange}
                                        name="roles"
                                        id="roles"
                                        value={values.roles}
                                        label="角色"
                                        fullWidth
                                        error={Boolean(touched.roles && errors.roles)}
                                    >
                                        <option value="">请选择角色</option>
                                        <option value="Admin">管理员</option>
                                        <option value="Faculty">教职工</option>
                                        <option value="Student">学生</option>
                                    </Select>
                                    {touched.roles && errors.roles && (
                                        <FormHelperText error id="helper-text-roles">
                                            {errors.roles}
                                        </FormHelperText>
                                    )}
                                </Stack>
                            </Grid>

                            <Grid item xs={12} md={6}>
                                <Stack spacing={1}>
                                    <InputLabel htmlFor="colleges">学院*</InputLabel>
                                    <Select
                                        native
                                        onChange={handleChange}
                                        name="colleges"
                                        id="colleges"
                                        value={values.colleges}
                                        label="学院"
                                        fullWidth
                                        error={Boolean(touched.colleges && errors.colleges)}
                                    >
                                        <option value="">请选择学院</option>
                                        <option value="COL1">学院1</option>
                                        <option value="COL2">学院2</option>
                                        <option value="COL3">学院3</option>
                                    </Select>
                                    {touched.colleges && errors.colleges && (
                                        <FormHelperText error id="helper-text-colleges">
                                            {errors.colleges}
                                        </FormHelperText>
                                    )}
                                </Stack>
                            </Grid>

                            <Grid item xs={12}>
                                <Stack spacing={1}>
                                    <InputLabel htmlFor="password">密码*</InputLabel>
                                    <OutlinedInput
                                        fullWidth
                                        error={Boolean(touched.password && errors.password)}
                                        id="password"
                                        type={showPassword ? 'text' : 'password'}
                                        name="password"
                                        onBlur={handleBlur}
                                        onChange={(e) => {
                                            handleChange(e);
                                            const temp = strengthIndicator(e.target.value);
                                            setLevel(strengthColor(temp));
                                        }}
                                        value={values.password}
                                        endAdornment={
                                            <InputAdornment position="end">
                                                <IconButton
                                                    aria-label="切换密码可见性"
                                                    onClick={handleClickShowPassword}
                                                    onMouseDown={handleMouseDownPassword}
                                                    edge="end"
                                                    size="large"
                                                >
                                                    {showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
                                                </IconButton>
                                            </InputAdornment>
                                        }
                                        placeholder="******"
                                    />
                                    {touched.password && errors.password && (
                                        <FormHelperText error id="helper-text-password">
                                            {errors.password}
                                        </FormHelperText>
                                    )}
                                </Stack>
                                {level && (
                                    <FormControl fullWidth sx={{ mt: 2 }}>
                                        <Grid container spacing={2} alignItems="center">
                                            <Grid item>
                                                <Box sx={{ bgcolor: level.color, width: 85, height: 8, borderRadius: '7px' }} />
                                            </Grid>
                                            <Grid item>
                                                <Typography variant="subtitle1" fontSize="0.75rem">
                                                    {level.label}
                                                </Typography>
                                            </Grid>
                                        </Grid>
                                    </FormControl>
                                )}
                            </Grid>

                            <Grid item xs={12}>
                                <Stack spacing={1}>
                                    <InputLabel htmlFor="passwordConfirm">确认密码*</InputLabel>
                                    <OutlinedInput
                                        fullWidth
                                        error={Boolean(touched.passwordConfirm && errors.passwordConfirm)}
                                        id="passwordConfirm"
                                        type={showPassword ? 'text' : 'password'}
                                        name="passwordConfirm"
                                        onBlur={handleBlur}
                                        onChange={handleChange}
                                        value={values.passwordConfirm}
                                        endAdornment={
                                            <InputAdornment position="end">
                                                <IconButton
                                                    aria-label="切换确认密码可见性"
                                                    onClick={handleClickShowPassword}
                                                    onMouseDown={handleMouseDownPassword}
                                                    edge="end"
                                                    size="large"
                                                >
                                                    {showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
                                                </IconButton>
                                            </InputAdornment>
                                        }
                                        placeholder="******"
                                    />
                                    {touched.passwordConfirm && errors.passwordConfirm && (
                                        <FormHelperText error id="helper-text-passwordConfirm">
                                            {errors.passwordConfirm}
                                        </FormHelperText>
                                    )}
                                </Stack>
                            </Grid>

                            <Grid item xs={12}>
                                <Typography variant="body2">
                                    注册即表示您同意我们的&nbsp;
                                    <Link variant="subtitle2" component={RouterLink} to="#">
                                        服务条款
                                    </Link>
                                    &nbsp;和&nbsp;
                                    <Link variant="subtitle2" component={RouterLink} to="#">
                                        隐私政策
                                    </Link>
                                </Typography>
                            </Grid>

                            {errors.submit && (
                                <Grid item xs={12}>
                                    <FormHelperText error>{errors.submit}</FormHelperText>
                                </Grid>
                            )}

                            <Grid item xs={12}>
                                <AnimateButton>
                                    <Button
                                        disableElevation
                                        disabled={isSubmitting}
                                        fullWidth
                                        size="large"
                                        type="submit"
                                        variant="contained"
                                        color="primary"
                                    >
                                        创建账号
                                    </Button>
                                </AnimateButton>
                            </Grid>
                        </Grid>
                    </form>
                )}
            </Formik>
        </>
    );
};

export default AuthRegister;

关键修正点说明

  1. 统一字段名:Formik初始值、控件name属性、验证规则中的字段名完全保持一致(比如username)。
  2. 移除冗余状态:删除formValues和formpassword,所有控件的value绑定Formik提供的values对象。
  3. 修复Select组件绑定:Select组件的value绑定values.roles/values.colleges,并添加对应的错误提示。
  4. 完善验证规则:为确认密码添加oneOf规则,确保两次密码一致。
  5. 优化提交逻辑:使用try/catch/finally处理提交过程,确保setSubmitting(false)始终执行。

内容的提问来源于stack exchange,提问作者sneha narvekar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 07:37:05