React MUI + Formik下拉Select无法选中值且默认值不生效问题
问题描述
我在React项目中使用MUI和Formik开发表单,尝试通过API返回的对象为Select组件设置默认值,但该组件无法正常工作:既无法选中选项,默认值也未被预设。同时我发现该组件的样式也存在异常,相关代码如下:
import { Box, Button, FormControl, InputLabel, Select, TextField } from "@mui/material"; import React, { useEffect, useState } from "react"; import { useLocation, useParams } from "react-router-dom"; import { Formik } from "formik"; import * as yup from "yup"; import useMediaQuery from "@mui/material/useMediaQuery"; import Header from "../../components/Header"; import useAuth from "../../hooks/useAuth"; import axios from "axios"; import { MenuItem } from "react-pro-sidebar"; const EditEmployee = () => { const { user_id } = useParams(); // employee id const [emp, setEmployee] = useState({}); const [Companies, setCompanies] = useState([]); const [defcompany, setDefcompany] = useState(""); const [CompanyId, setCompanyId] = useState(null); const handleChange = (event) => { setDefcompany(event.target.value); }; const { auth } = useAuth(); // get companies const GetCompanies = () => { axios.get(`${process.env.REACT_APP_MAIN_API_URL}/companies/`).then(function (res) { try { var result = res.data; // console.log(result); setCompanies(result); } catch (error) { console.log(error); } }); }; useEffect(() => { GetCompanies(); }, []); // get companies useEffect(() => { try { const response = axios .get(`${process.env.REACT_APP_MAIN_API_URL}/employees/` + user_id, { headers: { "Content-Type": "application/json", // Authorization: "Bearer " + auth.accessToken }, // withCredentials: true, }) .then((response) => { setEmployee(response.data[0]); setCompanyId(emp.company_id); console.log(response.data[0]); }); } catch (err) { console.log(err); if (!err?.response) { console.log("No server Response"); } else if (!err?.response?.status === 400) { console.log("Missing username or password"); } else if (!err?.response?.status === 401) { console.log("Unauthorized"); } else { console.log("Login Failed"); } } }, []); const phoneRegExp = /^((\+[1-9]{1,4}[ -]?)|(\([0-9]{2,3}\)[ -]?)|([0-9]{2,4})[ -]?)*?[0-9]{3,4}[ -]?[0-9]{3,4}$/; const userSchema = yup.object().shape({ first_name: yup.string().required("required"), last_name: yup.string().required("required"), email: yup.string().email("Invalid email").required("required"), contact_no: yup.string().matches(phoneRegExp, "Phone number is not valid").required("required"), // address1: yup.string().required("required"), // address2: yup.string().required("required"), }); const isNonMobile = useMediaQuery("(min-width:600px)"); const handleFormSubmit = (values) => { console.log("VALUES"); console.log(values); }; return ( <Box m="20px"> <Header title="Editing" subtitle={`${emp.first_name} ${emp.last_name} ( ${emp.employee_id} )`}></Header> <Formik enableReinitialize={true} onSubmit={handleFormSubmit} initialValues={emp} validationSchema={userSchema}> {({ values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( <form onSubmit={handleSubmit}> <Box display="grid" gap="30px" gridTemplateColumns="repeat(4,minmax(0,1fr))" sx={{ "& >div": { gridColumn: isNonMobile ? undefined : "span 4" }, }} > <TextField fullWidth variant="outlined" type="text" label="First Name" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.first_name} name="first_name" error={!!touched.first_name && !!errors.first_name} helperText={touched.first_name && errors.first_name} sx={{ gridColumn: "span 1" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Last Name" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.last_name} name="last_name" error={!!touched.last_name && !!errors.last_name} helperText={touched.last_name && errors.last_name} sx={{ gridColumn: "span 1" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Email" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.email} name="email" error={!!touched.email && !!errors.email} helperText={touched.email && errors.email} sx={{ gridColumn: "span 1" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Phone" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.contact_no} name="contact_no" error={!!touched.contact && !!errors.contact} helperText={touched.contact && errors.contact} sx={{ gridColumn: "span 1" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Username" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.username} name="username" readOnly error={!!touched.contact && !!errors.contact} helperText={touched.contact && errors.contact} sx={{ gridColumn: "span 1" }} size="small" /> <FormControl fullWidth> <InputLabel id="company_id">Company</InputLabel> <Select labelId="company_id" // InputLabelProps={{ shrink: true }} name="company_id" fullWidth id="demo-simple-select" value={CompanyId || ""} label="Company" onChange={(e) => setCompanyId(e.target.value)} size="small" > {Companies.map((company) => ( <MenuItem key={company.company_id} value={company.company_id}> {company.name} </MenuItem> ))} </Select> </FormControl> <TextField fullWidth variant="outlined" type="text" label="Department" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={emp.department_id} name="department_id" error={!!touched.contact && !!errors.contact} helperText={touched.contact && errors.contact} sx={{ gridColumn: "span 1" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Designation" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={emp.designation_id} name="designation_id" error={!!touched.contact && !!errors.contact} helperText={touched.contact && errors.contact} sx={{ gridColumn: "span 1" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Address 1" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.address1} name="address1" error={!!touched.address1 && !!errors.address1} helperText={touched.address1 && errors.address1} sx={{ gridColumn: "span 2" }} size="small" /> <TextField fullWidth variant="outlined" type="text" label="Address 2" InputLabelProps={{ shrink: true }} onBlur={handleBlur} onChange={handleChange} value={values.address2} name="address2" error={!!touched.address2 && !!errors.address2} helperText={touched.address2 && errors.address2} sx={{ gridColumn: "span 2" }} size="small" /> </Box> <Box display="flex" justifyContent="end" mt="20px"> <Button type="submit" color="secondary" variant="contained"> Update </Button> </Box> </form> )} </Formik> </Box> ); }; export default EditEmployee;
问题截图


解决方案
1. 核心问题:Select组件未接入Formik状态
你当前把Select的状态存在独立的CompanyId中,和Formik的全局values完全脱节,导致值无法同步、默认值不生效。
- 删掉
defcompany、CompanyId这两个独立状态 - Select组件的
value改用values.company_id,onChange用Formik提供的handleChange,同时加上onBlur={handleBlur}实现校验联动 - 确保API返回的
emp对象包含company_id字段,Formik会通过initialValues={emp}自动初始化该值
2. 修复异步初始化错误
获取员工数据的useEffect中,setEmployee是异步操作,你直接用emp.company_id会拿到初始空对象的undefined,导致CompanyId赋值无效。
- 不需要单独设置公司ID,接入Formik后会自动同步API返回的
company_id - 若需要单独处理,直接用
response.data[0].company_id而非emp.company_id
3. 修正MenuItem导入错误
你从react-pro-sidebar导入了MenuItem,但MUI的Select必须使用@mui/material的MenuItem,这是样式异常的根源。
- 修改导入语句:
import { Box, Button, FormControl, InputLabel, Select, TextField, MenuItem } from "@mui/material";
4. 修复表单错误提示复用问题
多个TextField的error和helperText错误复用了contact字段的校验结果,比如部门、职位字段,需改成对应字段的校验值:
- 部门字段:
error={!!touched.department_id && !!errors.department_id},helperText={touched.department_id && errors.department_id} - 职位字段:
error={!!touched.designation_id && !!errors.designation_id},helperText={touched.designation_id && errors.designation_id}
修复后的Select核心代码
<FormControl fullWidth sx={{ gridColumn: "span 1" }}> <InputLabel id="company_id-label">Company</InputLabel> <Select labelId="company_id-label" name="company_id" id="company_id" value={values.company_id || ""} label="Company" onChange={handleChange} onBlur={handleBlur} size="small" > {Companies.map((company) => ( <MenuItem key={company.company_id} value={company.company_id}> {company.name} </MenuItem> ))} </Select> </FormControl>
额外注意
- 保留Formik的
enableReinitialize={true},确保emp更新时Formik会重新同步初始值 - 可以在yup schema中添加
company_id的校验规则,比如yup.number().required("请选择公司"),完善表单校验逻辑
内容的提问来源于stack exchange,提问作者mahethekiller
相关产品推荐
相关产品推荐

