React+MaterialUI中Formik+Axios POST请求无法写入MySQL问题排查
在MaterialUI项目中使用useFormik Hook集成Formik,控制台日志显示onSubmit流程正常执行,但通过Axios发送的POST请求无法将新数据写入MySQL数据库。后端API接口经Postman测试可正常工作,需要定位问题。
相关代码如下:
import React, { useEffect } from 'react'; import { Box, Card, Container, Grid, Typography } from '@mui/material'; import Button from '@mui/material/Button'; import Paper from '@mui/material/Paper'; import { NavLink } from 'react-router-dom'; import PageTitle from '../pageTitle/PageTitle'; import TextField from '@mui/material/TextField'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { FormControl } from '@mui/material'; import { useFormik } from 'formik'; import * as yup from 'yup'; import axios from 'axios'; // import { Navigate } from 'react-router-dom'; const validationSchema = yup.object({ agencyName: yup .string() .min(8, 'Must be at least 8 characters') .max(20, 'Must be less than 20 characters') .required('Username is required') .matches(/^[a-zA-Z0-9]+$/, 'Cannot contain special characters or spaces'), phoneNumber: yup.string().required('Required'), // .matches(/^[0-9]*$/, 'Phone number is not valid') email: yup .string('Enter your email') .email('Enter a valid email') .required('Email is required'), streetAddress: yup.string().required('Required'), zipCode: yup.string().required('Required'), state: yup.string().required('Required'), country: yup.string().required('Required'), }); const AddHRAgency = () => { // const navigate = Navigate(); const formik = useFormik({ initialValues: { name: '', phone: '', email: '', unit_street_address: '', city_address: '', zip_code: '', state: '', country: '', }, // validationSchema: validationSchema, onSubmit: async (values, props) => { console.log('The Process Starts'); try { console.log('step1'); const userVal = JSON.stringify(values, null, 2); console.log(userVal); await axios.post('http://localhost:8800/AddHRAgencies', userVal, { 'content-type': 'application/json', }); console.log('step3'); props.resetForm(); props.setSubmitting(false); } catch (error) { console.log(`Error OCCURRED:: ${error}`); } console.log('The Process Ends'); }, }); return ( <React.Fragment> <Box sx={{ margin: '1rem', padding: '1rem', background: 'linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(194,203,217,1) 76%)', }} > <Paper sx={{ width: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-around', alignItems: 'center', height: '100%', background: 'linear-gradient(4deg, rgba(255,255,255,1) 0%, rgba(214,221,232,1) 81%)', }} variant='outlined' > <Box sx={{ display: 'flex', alignItems: 'space-evenly', justifyContent: 'space-evenly', flexWrap: 'wrap', gap: '1.5rem', marginTop: '1rem', // position: 'relative', }} > <PageTitle> <Typography variant='h3' sx={{}}> Add a New HR Agency Record </Typography> </PageTitle> <form // component='form' onSubmit={formik.handleSubmit} sx={{ // '& .MuiTextField-root': { m: 1, width: '25ch' }, margin: '1rem 0', width: '80%', }} > {/* <FormControl sx={{ width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', }} > */} <Grid container spacing={2}> <Grid item xs={12}> <TextField required id='outlined-basic' label='Name' variant='outlined' margin='normal' fullWidth type='text' name='name' value={formik.values.name} onChange={formik.handleChange} error={formik.touched.name && Boolean(formik.errors.name)} helperText={formik.touched.name && formik.errors.name} /> </Grid> <Grid item xs={12}> <TextField required id='outlined-basic' label='Agency Phone' variant='outlined' margin='normal' fullWidth type='text' name='phone' value={formik.values.phone} onChange={formik.handleChange} error={formik.touched.phone && Boolean(formik.errors.phone)} helperText={formik.touched.phone && formik.errors.phone} /> </Grid> <Grid item xs={12}> <TextField required id='outlined-basic' label='Agency Email' variant='outlined' margin='normal' fullWidth type='email' name='email' value={formik.values.email} onChange={formik.handleChange} error={formik.touched.email && Boolean(formik.errors.email)} helperText={formik.touched.email && formik.errors.email} /> </Grid> <Grid item xs={12}> <TextField id='outlined-basic' label='Unit Address' variant='outlined' margin='normal' fullWidth required type='text' name='unit_street_address' value={formik.values.unit_street_address} onChange={formik.handleChange} error={ formik.touched.unit_street_address && Boolean(formik.errors.unit_street_address) } helperText={ formik.touched.unit_street_address && formik.errors.unit_street_address } /> </Grid> <Grid item xs={12}> <TextField id='outlined-basic' label='Street Address' variant='outlined' margin='normal' fullWidth required type='text' name='city_address' value={formik.values.city_address} onChange={formik.handleChange} error={ formik.touched.city_address && Boolean(formik.errors.city_address) } helperText={ formik.touched.city_address && formik.errors.city_address } /> </Grid> <Grid item xs={12}> <TextField required id='outlined-basic' label='Zip code' variant='outlined' margin='normal' fullWidth type='text' name='zip_code' value={formik.values.zip_code} onChange={formik.handleChange} error={ formik.touched.zip_code && Boolean(formik.errors.zip_code) } helperText={ formik.touched.zip_code && formik.errors.zip_code } /> </Grid> <Grid item xs={12}> <TextField id='outlined-basic' label='State' variant='outlined' margin='normal' fullWidth required type='text' name='state' value={formik.values.state} onChange={formik.handleChange} error={formik.touched.state && Boolean(formik.errors.state)} helperText={formik.touched.state && formik.errors.state} /> </Grid> <Grid item xs={12}> <TextField id='outlined-basic' label='Country' variant='outlined' margin='normal' fullWidth required type='text' name='country' value={formik.values.country} onChange={formik.handleChange} error={ formik.touched.country && Boolean(formik.errors.country) } helperText={ formik.touched.country && formik.errors.country } /> </Grid> </Grid> <Button sx={{ marginTop: '2rem;', width: '25%', display: 'flex', justifyContent: 'center', alignItems: 'center', }} variant='contained' type='submit' > Submit </Button> {/* </FormControl> */} </form> {/* </form> */} <Button variant='contained' component={NavLink} to='/hragencies' sx={{ // marginTop: '2rem;', width: '20%', minWidth: '180px', display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '0.5rem 1.5rem', marginBottom: '1.5rem', }} > <ArrowBackIcon sx={{ marginRight: '1rem' }} /> HR Agency </Button> </Box> </Paper> </Box> </React.Fragment> ); }; export default AddHRAgency;
控制台日志显示流程完整执行:打印了"The Process Starts"、"step1"、表单值的JSON字符串、"step3"、"The Process Ends",无报错信息。
1. Axios请求数据格式错误
手动用JSON.stringify()将表单值转为字符串,但Axios默认会自动序列化JavaScript对象为JSON格式,同时自动设置Content-Type: application/json。手动转字符串后,后端接收到的是JSON字符串而非JSON对象,导致无法正确解析(Postman是直接传递JSON对象,所以能成功)。
修复:直接传递values对象,无需手动序列化:
await axios.post('http://localhost:8800/AddHRAgencies', values);
2. 请求头配置错误
原代码中content-type直接写在Axios配置的顶层,正确的写法应该放在headers对象内,否则请求头不会被正确设置。
修复:如果需要手动指定请求头,调整为:
await axios.post('http://localhost:8800/AddHRAgencies', values, { headers: { 'Content-Type': 'application/json' } });
注:由于Axios默认会处理,这一步其实可以省略。
3. Formik验证规则未启用且字段不匹配
定义了validationSchema但注释掉了,导致表单提交前没有验证,可能传递了不符合后端要求的数据;同时验证规则里的字段名(如agencyName、phoneNumber)和FormikinitialValues里的字段名(name、phone)不匹配,即使启用验证也无法生效。
修复:
- 取消注释
validationSchema: validationSchema, - 修改
validationSchema的字段名,和initialValues保持一致:
const validationSchema = yup.object({ name: yup .string() .min(8, 'Must be at least 8 characters') .max(20, 'Must be less than 20 characters') .required('Username is required') .matches(/^[a-zA-Z0-9]+$/, 'Cannot contain special characters or spaces'), phone: yup.string().required('Required'), email: yup .string('Enter your email') .email('Enter a valid email') .required('Email is required'), unit_street_address: yup.string().required('Required'), zip_code: yup.string().required('Required'), state: yup.string().required('Required'), country: yup.string().required('Required'), });
4. 手动调用setSubmitting冗余
Formik在异步onSubmit中会自动管理isSubmitting状态,无需手动调用props.setSubmitting(false),手动调用可能导致状态异常。
修复:删除props.setSubmitting(false)。
修改后的完整onSubmit代码
onSubmit: async (values, props) => { console.log('The Process Starts'); try { console.log('step1'); console.log(values); await axios.post('http://localhost:8800/AddHRAgencies', values); console.log('step3'); props.resetForm(); } catch (error) { console.log(`Error OCCURRED:: ${error.response?.data || error.message}`); } console.log('The Process Ends'); },
内容的提问来源于stack exchange,提问作者KunalD

