React编辑页面初始值不显示,刷新后恢复问题排查
React编辑页面首次加载数据不显示问题修复
问题描述
开发React编辑页面时,通过URL参数id从数据库查询数据,预期数据会填充到表单输入组件中,但首次进入页面时数据不显示,刷新页面后才正常加载。已添加错误处理逻辑,但未触发生效。
问题根源
- React Query缓存未区分不同ID:查询班级数据的
queryKey仅为["classData"],未包含动态的classId,导致路由切换到不同ID时,React Query复用旧缓存数据,首次加载展示错误内容。 - Formik未监听外部数据变化:Formik的
initialValues仅在组件初始化时赋值一次,当classData异步加载完成后,Formik不会自动更新表单值,导致表单仍为空。 - 错误处理未展示具体信息:当前错误提示为固定文本,无法定位具体失败原因。
- 教师数据查询错误文案有误:捕获错误时错误信息仍标注为“class data”,与实际查询资源不符。
修复方案
1. 优化React Query查询键
给班级数据的queryKey添加classId,确保每个ID对应独立缓存条目:
{ queryKey: ["classData", classId], // 新增classId区分不同查询 queryFn: async () => { try { const data = await fetchData(`class/${classId}`); return data; } catch (error) { throw new Error(`Failed to fetch class data: ${error.message}`); } }, }
2. 让Formik支持重新初始化
在Formik组件中添加enableReinitialize={true},同时给classData属性添加可选链操作符,避免数据未加载时出现undefined报错:
<Formik initialValues={{ className: classData?.className || "", division: classData?.division || "", batch: classData?.batch || "", fees: classData?.fees || "", subjects: classData?.subjects || [], teacherId: classData?.teacherId || "", }} enableReinitialize={true} // 开启后,initialValues变化时自动更新表单 // ...其他配置 >
3. 完善错误提示
将错误提示改为展示具体错误信息,方便定位问题:
{classError && <Error severity="error">{classError.message}</Error>}
4. 修正教师数据查询的错误文案
把教师数据查询的错误提示文案修正为对应资源:
{ queryKey: ["teacherData"], queryFn: async () => { try { const data = await fetchData("teacher/get-all"); return data; } catch (error) { throw new Error(`Failed to fetch teacher data: ${error.message}`); // 修正为teacher data } }, }
完整修改后的组件代码
// react imports import { useParams } from "react-router-dom"; // material-ui import { Button, FormHelperText, Grid, InputLabel, OutlinedInput, Stack, CircularProgress, Select, MenuItem, LinearProgress, } from "@mui/material"; // third party import * as Yup from "yup"; import { Formik, FieldArray } from "formik"; import axios from "axios"; import { useQueries } from "@tanstack/react-query"; // project import import AnimateButton from "components/@extended/AnimateButton"; import { base_url } from "utils/baseurl"; import { fetchData } from "utils/fetchData"; import Error from "utils/Error"; import { useNavigate } from "react-router-dom"; import Progress from "utils/Progress"; // ============================|| CLASS - EDIT FORM ||============================ // const ClassEdit = () => { const { id: classId } = useParams(); const navigate = useNavigate(); const [classResult, teacherResult] = useQueries({ queries: [ { queryKey: ["classData", classId], // 添加classId区分不同查询 queryFn: async () => { try { const data = await fetchData(`class/${classId}`); return data; } catch (error) { throw new Error(`Failed to fetch class data: ${error.message}`); } }, }, { queryKey: ["teacherData"], queryFn: async () => { try { const data = await fetchData("teacher/get-all"); return data; } catch (error) { throw new Error(`Failed to fetch teacher data: ${error.message}`); // 修正错误文案 } }, }, ], }); const { data: classData, error: classError, isPending: classIsPending } = classResult; const { data: teacherData, error: teacherError, isPending: teacherIsPending } = teacherResult; return ( <> {classError && <Error severity="error">{classError.message}</Error>} {/* 展示具体错误信息 */} {classIsPending ? ( <Progress /> ) : ( <Formik initialValues={{ className: classData?.className || "", division: classData?.division || "", batch: classData?.batch || "", fees: classData?.fees || "", subjects: classData?.subjects || [], teacherId: classData?.teacherId || "", }} enableReinitialize={true} // 开启表单重新初始化 validationSchema={Yup.object().shape({ className: Yup.number(), division: Yup.string().max(255), batch: Yup.number(), teacherId: Yup.string(), fees: Yup.number(), subjects: Yup.array(), })} onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => { try { const response = await axios.put(`${base_url}/class/${classId}`, values); if (!response.data.success) setErrors({ submit: response.data.message }); else navigate(`/class`); setStatus({ success: false }); setSubmitting(false); } catch (err) { console.error(err); setStatus({ success: false }); setErrors({ submit: err.message }); setSubmitting(false); } }} > {({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => ( <form noValidate onSubmit={handleSubmit}> <Grid container spacing={3}> <Grid item xs={12} md={6}> <Stack spacing={1}> <InputLabel htmlFor="class-name">Class Standard</InputLabel> <OutlinedInput fullWidth id="class-name" value={values.className} name="className" onBlur={handleBlur} onChange={handleChange} error={Boolean(touched.className && errors.className)} /> {touched.className && errors.className && ( <FormHelperText error id="helper-text-className-signup"> {errors.className} </FormHelperText> )} </Stack> </Grid> <Grid item xs={12} md={6}> <Stack spacing={1}> <InputLabel htmlFor="division">Division</InputLabel> <OutlinedInput fullWidth error={Boolean(touched.division && errors.division)} id="division" value={values.division} name="division" onBlur={handleBlur} onChange={handleChange} /> {touched.division && errors.division && ( <FormHelperText error id="helper-text-division-signup"> {errors.division} </FormHelperText> )} </Stack> </Grid> <Grid item xs={6}> <Stack spacing={1}> <InputLabel htmlFor="teacherId">Teacher</InputLabel> <Select fullWidth id="teacherId" value={values.teacherId} name="teacherId" onChange={handleChange} onBlur={handleBlur} error={touched.teacherId && errors.teacherId} > {teacherError && <MenuItem key="error">Cannot find teacher data</MenuItem>} {teacherIsPending ? ( <LinearProgress /> ) : ( teacherData?.map((teacher) => ( <MenuItem key={teacher._id} value={teacher._id}> {teacher.name} </MenuItem> )) )} </Select> {touched.teacherId && errors.teacherId && ( <FormHelperText error id="helper-text-teacherId-signup"> {errors.teacherId} </FormHelperText> )} </Stack> </Grid> <Grid item xs={6}> <Stack spacing={1}> <InputLabel htmlFor="batch">Batch Start</InputLabel> <OutlinedInput fullWidth error={Boolean(touched.batch && errors.batch)} id="batch" value={values.batch} name="batch" type="number" onBlur={handleBlur} onChange={handleChange} placeholder="2019" inputProps={{}} /> {touched.batch && errors.batch && ( <FormHelperText error id="helper-text-batch-signup"> {errors.batch} </FormHelperText> )} </Stack> </Grid> <Grid item xs={6}> <Stack spacing={1}> <InputLabel htmlFor="fees">Fees</InputLabel> <OutlinedInput fullWidth error={Boolean(touched.fees && errors.fees)} id="fees" value={values.fees} name="fees" type="number" onBlur={handleBlur} onChange={handleChange} inputProps={{}} /> {touched.fees && errors.fees && ( <FormHelperText error id="helper-text-fees-signup"> {errors.fees} </FormHelperText> )} </Stack> </Grid> <Grid item xs={6}> <Stack spacing={1} sx={{ mt: "8px" }}> <FieldArray name="subjects" render={(arrayHelpers) => ( <div> {values.subjects && values.subjects.map((subject, index) => ( <div key={index}> <InputLabel htmlFor={`subjects.${index}`}>Subject</InputLabel> <OutlinedInput fullWidth id={`subjects.${index}`} value={values.subjects[index]} name={`subjects.${index}`} onBlur={handleBlur} onChange={handleChange} /> <Button type="button" onClick={() => arrayHelpers.remove(index)}> Remove </Button> </div> ))} <Button type="button" onClick={() => arrayHelpers.push("")}> Add </Button> </div> )} /> </Stack> </Grid> <Grid item xs={12}> <AnimateButton> <Button disableElevation disabled={isSubmitting} size="large" type="submit" variant="contained" color="primary" > Edit </Button> </AnimateButton> </Grid> </Grid> </form> )} </Formik> )} </> ); }; export default ClassEdit;
额外注意点
- 检查
fetchData函数是否正确解析API响应,比如是否需要从response.data中提取数据主体(例如return data.data)。 - 确保路由配置中正确定义了参数路径,比如
path="/class/edit/:id"。
内容的提问来源于stack exchange,提问作者farismamnoon
相关产品推荐
相关产品推荐

