无法在 Material UI 中动态设置 Select 框的初始状态
Unable to dynamically set the initial state of a Select box in Material UI
我正在使用 Material UI 5.7.0。我创建了一个调查表,用户可以填写该表并单击按钮下载包含他们的答案的 json 文件。我想在名为 questionsObject
的 json 对象中管理我的问题。使用同一个对象(或从它动态创建的对象)来加载表单的初始状态以及更新它似乎是有意义的。
下面的代码有效,但我已经在 defaultValues 对象中静态定义了我的默认值。每次我向问题对象添加新问题时,我都需要更新它。我真的希望它从 questionsObject 的值字段中读取默认值。
工作代码
import React, {useEffect, useState} from "react";
import Grid from "@mui/material/Grid";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
import RadioGroup from "@mui/material/RadioGroup";
import Radio from "@mui/material/Radio";
import Button from "@mui/material/Button";
import Select from '@mui/material/Select';
import { saveAs } from 'file-saver';
import {InputLabel, MenuItem} from "@mui/material";
const questionsObject = {
q1: {
questionText: "Animals",
type: "BOTH",
displayType: "select",
menuItems: [
'',
"Dogs",
"Cats",
"Elephants"
],
value: ''
},
q2: {
questionText: "Favorite Pizza Topping",
type: "BOTH",
displayType: "select",
menuItems: [
'',
"Pepperoni",
"Mushrooms"
],
value: ''
},
q3: {
questionText: "Question text",
type: "BOTH",
displayType: "radioGroup",
value: "true"
},
q4: {
questionText: "Question text",
type: "BOTH",
displayType: "radioGroup",
value: "no answer"
},
q5: {
questionText: "Question text",
type: "BE",
displayType: "radioGroup",
value: "no answer"
}
}
const defaultValues = {
q1: '',
q2: '',
q3: "no answer",
q4: "no answer",
q5: "no answer"
}
const MyForm = () => {
const [formValues, setFormValues] = useState(defaultValues);
// useEffect(() => {
// const outputObj = {}
// Object.keys(questionsObject).map((question) =>
// outputObj[question.id] = question.value
// );
// setFormValues(outputObj)
// }, []);
const handleInputChange = (e) => {
const { name, value} = e.target;
setFormValues({
...formValues,
[name]: value
});
};
const handleSubmit = (event) => {
event.preventDefault();
const outputObj = {}
Object.keys(questionsObject).map((question) =>
outputObj[question.id] = {
questionText: question.questionText,
type: question.type,
answer: formValues[question.id]
}
);
console.log(outputObj);
const outputObjJSON = JSON.stringify(outputObj, null, 2);
const blob = new Blob([outputObjJSON], {type: "application/json"});
saveAs(blob, "answerfile.json");
};
const getQuestionElement = (questionId) => {
const question = questionsObject[questionId];
switch(question.displayType) {
case "radioGroup":
return (
<Grid item key={questionId}>
<FormControl>
<FormLabel>{question.questionText}</FormLabel>
<RadioGroup
questiontype = {question.type}
name={questionId}
value={formValues[questionId]}
onChange={handleInputChange}
row
>
<FormControlLabel
value="no answer"
control={<Radio size="small" />}
label="No Answer"
/>
<FormControlLabel
value="false"
control={<Radio size="small" />}
label="False"
/>
<FormControlLabel
value="true"
control={<Radio size="small" />}
label="True"
/>
<FormControlLabel
value="NA"
control={<Radio size="small" />}
label="Not Applicable"
/>
</RadioGroup>
</FormControl>
</Grid>
);
case "select":
return (
<Grid item key={questionId}>
<FormControl sx={{ m: 2, minWidth: 200 }}>
<InputLabel>{question.questionText}</InputLabel>
<Select
name={questionId}
value={formValues[questionId]}
label={question.questionText}
onChange={handleInputChange}
>
{question.menuItems.map((item, index) =>
<MenuItem key={index} value={item}>{item}</MenuItem>
)}
</Select>
</FormControl>
</Grid>
);
default:
}
}
return formValues ? (
<form onSubmit={handleSubmit}>
<Grid
container
spacing={0}
direction="column"
alignItems="left"
justifyContent="center">
{Object.keys(questionsObject).map((questionId =>
getQuestionElement(questionId)
))}
<Button variant="contained" color="primary" type="submit">
Download
</Button>
</Grid>
</form>
): null ;
};
export default MyForm;
我尝试过的事情
我试过像这样从没有默认状态开始:
const [formValues, setFormValues] = useState();
并取消注释 useEffect()
但我得到这个错误:
MUI: You have provided an out-of-range value `undefined` for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are ``, `Dogs`, `Cats`, `Elephants`.
此外,我的单选按钮没有设置默认值。
这导致我尝试在 RadioGroup 和 Select 组件上使用 defaultValue 字段。
正在尝试像这样在默认值 属性 中设置初始值:
defaultValue=""
或
defaultValue={question.value}
给我这个错误:
MUI: A component is changing the uncontrolled value state of Select to be controlled.
Elements should not switch from uncontrolled to controlled (or vice versa).
Decide between using a controlled or uncontrolled Select element for the lifetime of the component.
The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.
如何删除 defaultValues 对象并使用 questionsObject 中的值字段代替初始状态?
问题出在你的逻辑中 useEffect
:
useEffect(() => {
const outputObj = {}
Object.keys(questionsObject).map(
(question) => outputObj[question.id] = question.value
);
console.log(outputObj);
setFormValues(outputObj)
}, []);
/// OUTPUT of outputObj:
{undefined: undefined}
您应该将其更改为:
useEffect(() => {
const outputObj = {};
Object.keys(questionsObject).map(
(question) => (outputObj[question] = questionsObject[question].value)
);
console.log(outputObj);
setFormValues(outputObj);
}, []);
/// OUTPUT of outputObj:
{q1: "", q2: "", q3: "true", q4: "no answer", q5: "no answer"}
此外,避免使用 switch/case
- 主要是为了不对 default
值做任何事情。在这种情况下,更喜欢使用 &&
运算符 (docs) 的条件渲染:
const getQuestionElement = (questionId) => {
const question = questionsObject[questionId];
return (
<React.Fragment key={questionId}>
{question.displayType === "radioGroup" && (
<Grid item>
<FormControl>
<FormLabel>{question.questionText}</FormLabel>
<RadioGroup
questiontype={question.type}
name={questionId}
value={formValues[questionId]}
onChange={handleInputChange}
row
>
<FormControlLabel
value="no answer"
control={<Radio size="small" />}
label="No Answer"
/>
<FormControlLabel
value="false"
control={<Radio size="small" />}
label="False"
/>
<FormControlLabel
value="true"
control={<Radio size="small" />}
label="True"
/>
<FormControlLabel
value="NA"
control={<Radio size="small" />}
label="Not Applicable"
/>
</RadioGroup>
</FormControl>
</Grid>
)}
{question.displayType === "select" && (
<Grid item>
<FormControl sx={{ m: 2, minWidth: 200 }}>
<InputLabel>{question.questionText}</InputLabel>
<Select
name={questionId}
value={formValues[questionId]}
label={question.questionText}
onChange={handleInputChange}
>
{question.menuItems.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
)}
</React.Fragment>
);
};
我正在使用 Material UI 5.7.0。我创建了一个调查表,用户可以填写该表并单击按钮下载包含他们的答案的 json 文件。我想在名为 questionsObject
的 json 对象中管理我的问题。使用同一个对象(或从它动态创建的对象)来加载表单的初始状态以及更新它似乎是有意义的。
下面的代码有效,但我已经在 defaultValues 对象中静态定义了我的默认值。每次我向问题对象添加新问题时,我都需要更新它。我真的希望它从 questionsObject 的值字段中读取默认值。
工作代码
import React, {useEffect, useState} from "react";
import Grid from "@mui/material/Grid";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
import RadioGroup from "@mui/material/RadioGroup";
import Radio from "@mui/material/Radio";
import Button from "@mui/material/Button";
import Select from '@mui/material/Select';
import { saveAs } from 'file-saver';
import {InputLabel, MenuItem} from "@mui/material";
const questionsObject = {
q1: {
questionText: "Animals",
type: "BOTH",
displayType: "select",
menuItems: [
'',
"Dogs",
"Cats",
"Elephants"
],
value: ''
},
q2: {
questionText: "Favorite Pizza Topping",
type: "BOTH",
displayType: "select",
menuItems: [
'',
"Pepperoni",
"Mushrooms"
],
value: ''
},
q3: {
questionText: "Question text",
type: "BOTH",
displayType: "radioGroup",
value: "true"
},
q4: {
questionText: "Question text",
type: "BOTH",
displayType: "radioGroup",
value: "no answer"
},
q5: {
questionText: "Question text",
type: "BE",
displayType: "radioGroup",
value: "no answer"
}
}
const defaultValues = {
q1: '',
q2: '',
q3: "no answer",
q4: "no answer",
q5: "no answer"
}
const MyForm = () => {
const [formValues, setFormValues] = useState(defaultValues);
// useEffect(() => {
// const outputObj = {}
// Object.keys(questionsObject).map((question) =>
// outputObj[question.id] = question.value
// );
// setFormValues(outputObj)
// }, []);
const handleInputChange = (e) => {
const { name, value} = e.target;
setFormValues({
...formValues,
[name]: value
});
};
const handleSubmit = (event) => {
event.preventDefault();
const outputObj = {}
Object.keys(questionsObject).map((question) =>
outputObj[question.id] = {
questionText: question.questionText,
type: question.type,
answer: formValues[question.id]
}
);
console.log(outputObj);
const outputObjJSON = JSON.stringify(outputObj, null, 2);
const blob = new Blob([outputObjJSON], {type: "application/json"});
saveAs(blob, "answerfile.json");
};
const getQuestionElement = (questionId) => {
const question = questionsObject[questionId];
switch(question.displayType) {
case "radioGroup":
return (
<Grid item key={questionId}>
<FormControl>
<FormLabel>{question.questionText}</FormLabel>
<RadioGroup
questiontype = {question.type}
name={questionId}
value={formValues[questionId]}
onChange={handleInputChange}
row
>
<FormControlLabel
value="no answer"
control={<Radio size="small" />}
label="No Answer"
/>
<FormControlLabel
value="false"
control={<Radio size="small" />}
label="False"
/>
<FormControlLabel
value="true"
control={<Radio size="small" />}
label="True"
/>
<FormControlLabel
value="NA"
control={<Radio size="small" />}
label="Not Applicable"
/>
</RadioGroup>
</FormControl>
</Grid>
);
case "select":
return (
<Grid item key={questionId}>
<FormControl sx={{ m: 2, minWidth: 200 }}>
<InputLabel>{question.questionText}</InputLabel>
<Select
name={questionId}
value={formValues[questionId]}
label={question.questionText}
onChange={handleInputChange}
>
{question.menuItems.map((item, index) =>
<MenuItem key={index} value={item}>{item}</MenuItem>
)}
</Select>
</FormControl>
</Grid>
);
default:
}
}
return formValues ? (
<form onSubmit={handleSubmit}>
<Grid
container
spacing={0}
direction="column"
alignItems="left"
justifyContent="center">
{Object.keys(questionsObject).map((questionId =>
getQuestionElement(questionId)
))}
<Button variant="contained" color="primary" type="submit">
Download
</Button>
</Grid>
</form>
): null ;
};
export default MyForm;
我尝试过的事情
我试过像这样从没有默认状态开始:
const [formValues, setFormValues] = useState();
并取消注释 useEffect() 但我得到这个错误:
MUI: You have provided an out-of-range value `undefined` for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are ``, `Dogs`, `Cats`, `Elephants`.
此外,我的单选按钮没有设置默认值。
这导致我尝试在 RadioGroup 和 Select 组件上使用 defaultValue 字段。
正在尝试像这样在默认值 属性 中设置初始值:
defaultValue=""
或
defaultValue={question.value}
给我这个错误:
MUI: A component is changing the uncontrolled value state of Select to be controlled.
Elements should not switch from uncontrolled to controlled (or vice versa).
Decide between using a controlled or uncontrolled Select element for the lifetime of the component.
The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.
如何删除 defaultValues 对象并使用 questionsObject 中的值字段代替初始状态?
问题出在你的逻辑中 useEffect
:
useEffect(() => {
const outputObj = {}
Object.keys(questionsObject).map(
(question) => outputObj[question.id] = question.value
);
console.log(outputObj);
setFormValues(outputObj)
}, []);
/// OUTPUT of outputObj:
{undefined: undefined}
您应该将其更改为:
useEffect(() => {
const outputObj = {};
Object.keys(questionsObject).map(
(question) => (outputObj[question] = questionsObject[question].value)
);
console.log(outputObj);
setFormValues(outputObj);
}, []);
/// OUTPUT of outputObj:
{q1: "", q2: "", q3: "true", q4: "no answer", q5: "no answer"}
此外,避免使用 switch/case
- 主要是为了不对 default
值做任何事情。在这种情况下,更喜欢使用 &&
运算符 (docs) 的条件渲染:
const getQuestionElement = (questionId) => {
const question = questionsObject[questionId];
return (
<React.Fragment key={questionId}>
{question.displayType === "radioGroup" && (
<Grid item>
<FormControl>
<FormLabel>{question.questionText}</FormLabel>
<RadioGroup
questiontype={question.type}
name={questionId}
value={formValues[questionId]}
onChange={handleInputChange}
row
>
<FormControlLabel
value="no answer"
control={<Radio size="small" />}
label="No Answer"
/>
<FormControlLabel
value="false"
control={<Radio size="small" />}
label="False"
/>
<FormControlLabel
value="true"
control={<Radio size="small" />}
label="True"
/>
<FormControlLabel
value="NA"
control={<Radio size="small" />}
label="Not Applicable"
/>
</RadioGroup>
</FormControl>
</Grid>
)}
{question.displayType === "select" && (
<Grid item>
<FormControl sx={{ m: 2, minWidth: 200 }}>
<InputLabel>{question.questionText}</InputLabel>
<Select
name={questionId}
value={formValues[questionId]}
label={question.questionText}
onChange={handleInputChange}
>
{question.menuItems.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
)}
</React.Fragment>
);
};