Im dynamically generating material ui form components and i want to update their values when i write in the form. However the value prop is assigned to a useState values object. when i change this object and update the state, The value in the object changes correctly but the material UI component does not as its value is tied to the original value object. I believe its related to the fact the i am dynamically creating these components with a button.
The value prop in the text Field is what is not updating.
I am updating the values for these fields inside handleAnswersChange. it checks if an answer already exist. If it does it will update it. If not it will create a new one.
interface State {
title: string;
question: string;
answers: Array<Answer>;
correctAnswer: number;
}
interface Answer {
answer: string;
id: number;
}
export default function CreateQuestion() {
const [answers, setAnswers] = React.useState<Array<JSX.Element>>([]);
const [errorAnswer, setErrorAnswer] = React.useState(false);
const [values, setValues] = React.useState<State>({
title: "",
question: "",
answers: [{ answer: "", id: 1 }],
correctAnswer: 1,
});
useEffect(() => {
console.log(values);
}, [values]);
const handleChange =
(prop: any) => (event: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [prop]: event.target.value });
};
const handleAnswersChange = (
prop: any,
id: number
) => (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(values.answers[0].answer);
let answerIndex: number = 0;
let doesExist: boolean = false;
values.answers.map((answer, index) => {
if (answer.id === id) {
answerIndex = index;
doesExist = true;
}
});
if (doesExist) {
const tempValues = values.answers;
tempValues[answerIndex] = {
answer: tempValues[answerIndex].answer event.target.value,
id: id,
};
setValues({
...values,
[prop]: [...tempValues],
});
} else {
setValues({
...values,
[prop]: [...values.answers, { answer: event.target.value, id: id }],
});
}
};
const createAnswer = () => {
const array = [...answers];
array.push(
<Box sx={{ m: 1 }} key={answers.length}>
<FormControl
sx={{ width: "60ch" }}
variant="outlined"
key={answers.length}
>
<TextField
id="outlined-required"
key={answers.length}
label={"Answer " (answers.length 1)}
value={values.answers[answers.length - 1].answer}
onChange={
handleAnswersChange("answers", values.answers.length);
}
/>
</FormControl>
</Box>
CodePudding user response:
You are using the wrong index. You map over it, but also you the same static index. So formik will put it into the wrong place.
handleAnswersChange(event, "answers", values.answers.length);
This will always select the wrong index. Since the first element will have values.answers.length
of 1, but its index is 0.
Try it with .map and the index like this.
Save the answers in themselves and just render them with map.
You also do not need so many keys. Just for the direct child of map.
Update:
I just saw that you are already saving the answers in the values as well, so you can simply map over values.answers
and discard the answer array.