为从具有动态长度的字符串数组映射的 React 组件设置键
Setting keys for React components mapped from a string array with dynamic length
问题
这是一个问题表格,一个问题有很多答案。用户可以添加、编辑、删除表单中的答案。
答案存储在字符串数组中。我们将使用此数组来呈现答案输入及其对应的“删除”按钮。
我试过的:
- 将索引设置为键:当从数组中删除一个元素时,React 无法呈现剩余的问题(它删除了错误的元素),尽管来自
useState
的值计算正确。
- Set value as key:输入元素有变化的文本重新渲染,因此每次我们输入一个字符时它都会失去焦点。
我们如何解决这个问题?
代码沙盒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>
}
我想你忘了将你的答案值绑定到输入元素。现在你所有的输入都是不受控制的,这不是处理你添加或删除项目的动态表单的最佳方式,最好让它们受到控制。
就这样做:
<input
type="text"
value={answer}
onChange={(e) => onAnswerChangeAtIndex(e.target.value, index)}
/>
这里的其他好的做法可能是使用一些其他结构作为答案,例如,使用 id
创建一个对象而不是字符串(您可以自己生成它们,最简单的方法是使用 Math.random()
) 和 value
属性 每个答案,这样你就可以使用那个 id 作为真正的密钥。
问题
这是一个问题表格,一个问题有很多答案。用户可以添加、编辑、删除表单中的答案。
答案存储在字符串数组中。我们将使用此数组来呈现答案输入及其对应的“删除”按钮。
我试过的:
- 将索引设置为键:当从数组中删除一个元素时,React 无法呈现剩余的问题(它删除了错误的元素),尽管来自
useState
的值计算正确。 - Set value as key:输入元素有变化的文本重新渲染,因此每次我们输入一个字符时它都会失去焦点。
我们如何解决这个问题?
代码沙盒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>
}
我想你忘了将你的答案值绑定到输入元素。现在你所有的输入都是不受控制的,这不是处理你添加或删除项目的动态表单的最佳方式,最好让它们受到控制。
就这样做:
<input
type="text"
value={answer}
onChange={(e) => onAnswerChangeAtIndex(e.target.value, index)}
/>
这里的其他好的做法可能是使用一些其他结构作为答案,例如,使用 id
创建一个对象而不是字符串(您可以自己生成它们,最简单的方法是使用 Math.random()
) 和 value
属性 每个答案,这样你就可以使用那个 id 作为真正的密钥。