如何编辑此TextField



我有一个Material UI文本字段,该字段由从API获取的嵌套JSON对象填充。

数据可以显示在TextField、Date Picker或Select框中。这是由FieldType决定的。

数据显示在TextField的、日期选择器&选择框很好,但不能更改。试图更改任何输入中的文本都会导致此错误消息:Uncaught TypeError: prev.fields is not iterable

以下是我在各自输入中显示数据的方法。

{details["groups"]?.map((group) => {
return (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{group?.GroupName}</Typography>
</AccordionSummary>
<AccordionDetails>
<Box>
{group["fields"]?.map((row, index) => {
if (
row?.FieldType === "Text" ||
row?.FieldType === "Decimal" ||
row?.FieldType === "Number"
) {
return (
<TextField
value={row?.Value || ""}
onChange={(e) => {
setDetails((prev) => {
const update = [...prev.fields];
update[index] = {
...update[index],
Value: e.target.value,
};
return { ...prev, fields: update
});
}}
margin="normal"
label={row["FieldName"]}
/>
);
}
if (row?.FieldType === "Date") {
return (
<TextField
type="date"
value={row?.Value || null}
onChange={(e) => {
setDetails((prev) => {
const update = [...prev.fields];
update[index] = {
...update[index],
Value: e.target.value,
};
return { ...prev, fields: update 
});
}}
label={row["FieldName"]}
InputLabelProps={{
shrink: true,
}}
/>
);
} else {
return (
<TextField
value={row?.Value || ""}
onChange={(e) => {
setDetails((prev) => {
const update = [...prev.fields];
update[index] = {
...update[index],
Value: e.target.value,
};
return { ...prev, fields: update 
});
}}
select
label={row?.FieldName}
>
{row?.Choices.map((choice) => (
<MenuItem key={choice} value= {choice}>
{choice}
</MenuItem>
))}
</TextField>
);
}
})}
</Box>
</AccordionDetails>
</Accordion>
);
})}

我的JSON示例:

{
"groups": [
{
"GroupName": "Details",
"GroupOrder": 1,
"fields": [
"FieldId": 2,
"FieldName": "Day",
"FieldType": "Select",
"Value": "Option1",
"Choices": [
"Option1",
"Option2"
]
]
},
{
"GroupName": "Attributes",
"GroupOrder": 2,
"fields": [
{
"FieldId": 2,
"FieldName": "Night",
"FieldType": "Text",
"Value": "Night time",
"Choices": [
null
]
},
{
"FieldId": 3,
"FieldName": "Todays Date",
"FieldType": "Date",
"Value": "2020-08-12",
"Choices": [
null
]
}
],
}
]
}

API调用:

const params = useParams();
const [details, setDetails] = useState('');
const fetchDetails = async () => {
setDetails(
await fetch(`/fiscalyears/FY2023/intakes/${params.id}/details`).then(
(response) => response.json()
)
);
};
useEffect(() => {
fetchDetails();
}, []);

是否可以使用一种方法为多个嵌套的JSON对象创建textFields,而不是对其进行硬编码?

您只需要对onChange函数进行一些小的更改,并在映射函数上传递"index"参数

{questions["Days"]?.map((row, index) => (
<TextField
fullWidth
multiline
className="text-field"
value={row?.Response || ""}
onChange={(e) => {
setQuestions((prev) => {
const days = [...prev.Days];
days[index] = {
...days[index],
Response: e.target.value
};
return { ...prev, Days: days };
});
}}
variant="outlined"
margin="normal"
label={row["Question"]}
/>
))}

用你的功能控制台更新时的状态,然后用我提供的功能控制台,你会看到区别。

最终重构了代码。很多代码都可以写得更好。

Uncaught TypeError: prev.fields is not iterable.是因为您还没有通过handleChange中的groupIndex。

工作解决方案被放到Github repo中。

浏览一下主代码:

// noinspection JSIgnoredPromiseFromCall
import './App.css';
import {Accordion, AccordionDetails, AccordionSummary, Box, MenuItem, TextField, Typography} from "@mui/material";
import {useEffect, useState} from "react";
import YourJSON from "./YourJSON";
const App = () => {
const [details, setDetails] = useState({});
const fetchDetails = async () => {
// imitate an api call
setDetails(await YourJSON());
};
const generalFieldTypes = (fieldType) => {
return ["Text", "Decimal", "Number"].includes(fieldType);
}
const dateFieldTypes = (fieldType) => {
return fieldType === 'Date';
}
const handleChange = (e, fieldIndex, groupIndex) => {
console.log(JSON.stringify(details));
let update = details;
update['groups'][groupIndex]['fields'][fieldIndex]['Value'] = e.target.value;
console.log(JSON.stringify(update));
setDetails(update);
}
const DynamicField = (field, fieldIndex, groupIndex) => {
const {FieldId, FieldName, FieldType, Value, Choices} = field;
if (generalFieldTypes(FieldType)) {
return <TextField
defaultValue={Value}
onChange={
(e) => handleChange(e, fieldIndex, groupIndex)
}
label={FieldName}
InputLabelProps={{shrink: true}}
/>;
} else if (dateFieldTypes(FieldType)) {
return <TextField
type="date"
defaultValue={Value}
onChange={
(e) => handleChange(e, fieldIndex, groupIndex)
}
label={FieldName}
InputLabelProps={{shrink: true}}
/>;
} else {
return <TextField
defaultValue={Value}
onChange={
(e) => handleChange(e, fieldIndex, groupIndex)
}
select
label={FieldName}
>
{
Choices.map((choice) => (
<MenuItem key={choice} value={choice}>
{choice}
</MenuItem>
))
}
</TextField>;
}
}
useEffect(() => {
fetchDetails();
}, []);
return (
<div className="App">
{details && details["groups"]?.map((group, groupIndex) => {
const {GroupName, GroupOrder, fields} = group;
return (
<Accordion>
{/*I won't have your expandIcon, thereby remove it*/}
<AccordionSummary>
<Typography>{GroupName}</Typography>
</AccordionSummary>
<AccordionDetails>
<Box>
{
fields.map((field, fieldIndex) => {
return DynamicField(field, fieldIndex, groupIndex);
})
}
</Box>
</AccordionDetails>
</Accordion>
);
}
)}
</div>
);
}
export default App;