Problem
This is a question form, a question has many answers. User can add, edit, remove the answers in the form.
The answers is stored in a string array. We will use this array to render the answer input and its corresponding "remove" button.
What I've tried:
useState
computed correctly.How can we solve this?
CodeSandbox link: https://codesandbox.io/s/multiplechoicequestionform-2h0vp?file=/src/App.js
import { useState } from "react";
function MultipleChoiceQuestionForm() {
const [answers, setAnswers] = useState<string[]>([]);
const addAnswer = () => setAnswers([...answers, ""]); // Add a new empty one at bottom
const removeAnswerAtIndex = (targetIndex: number) => {
setAnswers(answers.filter((_, index) => index !== targetIndex));
};
const onAnswerChangeAtIndex = (newAnswer: string, targetIndex: number) => {
const newAnswers = [...answers];
newAnswers[targetIndex] = newAnswer;
setAnswers(newAnswers)
};
return <form>
{answers.map((answer, index) =>
<div
// I think the problem is the key, how to set this correctly ?
// Set to index: make removing elements has re-render errors
key={index}
// key={answer} // Lose focus on each character typed
style={{ display: "flex" }}
>
<input type="text" onChange={(e) => onAnswerChangeAtIndex(e.target.value, index)} />
<button onClick={(_) => removeAnswerAtIndex(index)}>Remove</button>
</div>
)}
<button onClick={addAnswer}>Add</button>
</form>
}
I think you forgot to bind your answer values to the input element. Right now all your inputs are uncontrolled which is not the best way to handle dynamic forms where you add or delete items, better make them controlled.
Just do that:
<input
type="text"
value={answer}
onChange={(e) => onAnswerChangeAtIndex(e.target.value, index)}
/>
Other good practice here might be using some other structure for answers, for example, instead of string create an object with id
(you can generate them by yourself, as an easiest way just use Math.random()
) and value
property for each answer, that way you could use that id as real key.