我是一名新手,我正在准备一个看起来像这样的表格:左边是实际的输入字段,右边是用户正在键入内容的实时预览。
表单名称及其用途有2个输入字段,(这是棘手的部分)一个添加问题的按钮,每个按钮都有2个输入字段,分别用于输入名称和描述。
我有2个用于操纵状态的函数,其中一个函数将一个空字符串对象(用于标题和描述)添加到问题数组。看起来像这样:
addQuestion = (event)=>{
event.preventDefault();
this.setState((prevState) => ({
questions: [...prevState.questions, {questionTitle: "", questionDescription: ""}]
}));
}
另一个取用户输入值并将其设置为标题和描述的状态。
setQuestion(question){
this.setState((prevState) => ({
questions: [{questionTitle: this.questionTitle.value, questionDescription: this.questionDescription.value}]
}));
}
现在,当我添加多个问题并尝试在输入字段中键入内容时,这些问题将消失,因为仅对问题数组中的一个对象设置了状态。如果我使用散布运算符,则因为它是实时表单而无法使用,所以第二个函数在更改时被调用,因此每次击键都等于一个新问题。
以下是在输入字段更改时映射状态的代码段:
const QuizQuestion = props=>{
return(
<div className="quiz-question">
<h2 className="question-title">{props.questionTitle}</h2>
<p className="question-description">{props.questionDescription}</p>
</div>
)
}
/*this is located in the render method*/
{this.state.questions.map((element, i)=>(
<QuizQuestion
key={i}
item={element}
questionTitle={element.questionTitle}
questionDescription={element.questionDescription}
/>
))}
我在做什么错? 预先感谢您的帮助!
答案 0 :(得分:0)
在您的示例中,setQuestion()
是在您更改输入时调用的,即用于onChange
事件处理。在每次调用时,setQuestion()
重复覆盖question
对象的state
数组。因此,将第二个表单数据添加到数组的第一个元素时,就没有问题,因为question
数组中最初没有元素。因此,数组的第一个元素会不断被覆盖,最后,当您单击“添加问题”按钮时,addQuestion()
将新元素添加到数组中。
问题在于,一旦您开始以第三种形式键入数据,就会调用setQuestion()
方法,该方法将更新您的状态,并且question
数组将被重置。因此,我合并了,我使用...prevstate.question
将先前的状态数据与新的状态数据合并。
但是坚持下去会引起另一个问题。现在,每次更改输入值时,您的question
数组将填充具有空值对{ questionTitle: "", questionDescription: "" }
的对象。但是,不会反映您在状态对象中输入的当前数据。这是由于以下事实:最初,表单中所有输入的值默认为空。因此,在更改输入时调用setQuestion
时,以前的对象现在将与新对象递归合并。这会导致在按下每个键时自动且无用地在数组中添加元素。另外还会丢失新表格,这是副作用(因为您在map()
上使用了this.state.question
来生成新表格)
因此,我想出了一个技巧来克服这个问题。只需使用question
删除this.state
的{{1}}数组的this.state.questions.splice(-1)
数组的最后一个元素,然后再更新调用this.setState()
以更新状态信息。
请参见下面的代码。这是codesandbox
上的演示
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const QuizHead = props => {
return (
<div className="quiz-head">
<h1 className="title">{props.title}</h1>
<p className="description">{props.description}</p>
</div>
);
};
const QuizQuestion = props => {
return (
<div className="quiz-question">
<h2 className="question-title">{props.questionTitle}</h2>
<p className="question-description">{props.questionDescription}</p>
</div>
);
};
class App extends Component {
constructor() {
super();
this.state = {
title: "",
description: "",
questions: []
};
this.setQuestion = this.setQuestion.bind(this);
this.setHead = this.setHead.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
setHead = event => {
event.preventDefault();
this.setState({
title: this.title.value,
description: this.description.value,
questions: []
});
};
addQuestion = event => {
event.preventDefault();
console.log("addquestion prev state", this.state);
this.setState(prevState => ({
questions: [
...prevState.questions,
{ questionTitle: "", questionDescription: "" }
]
}));
this.state.questions.map((element, i) =>
console.log(element.questionTitle)
);
};
setQuestion(question) {
this.state.questions.splice(-1);
this.setState(prevState => ({
questions: [
...prevState.questions,
{
questionTitle: this.questionTitle.value,
questionDescription: this.questionDescription.value
}
]
}));
console.log("set question", this.state);
}
render() {
return (
<div className="App">
<article className="forms">
<form className="head-form" onChange={this.setHead}>
<input
type="text"
ref={titleInput => {
this.title = titleInput;
}}
/>
<textarea
name="description"
ref={descriptionInput => {
this.description = descriptionInput;
}}
/>
</form>
<section className="questions-section">
{console.log('Form state',this.state)}
{this.state.questions.map((element, i) => (
<form
key={i}
item={element}
className="question-form"
onChange={this.setQuestion}
>
<input
className="question-title"
type="text"
ref={questionTitleInput => {
this.questionTitle = questionTitleInput;
}}
/>
<input
className="question-description"
ref={questionDescriptionInput => {
this.questionDescription = questionDescriptionInput;
}}
type="text"
/>
</form>
))}
<button className="add-question" onClick={this.addQuestion}>
Add Question
</button>
</section>
</article>
<article className="final-quiz">
<QuizHead
title={this.state.title}
description={this.state.description}
/>
{this.state.questions.map((element, i) => (
<QuizQuestion
key={i}
item={element}
questionTitle={element.questionTitle}
questionDescription={element.questionDescription}
/>
))}
</article>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
重要提示
根据React Documentation,状态应视为不可变的对象。
因此,修改作为this.state
上的属性存在的数组元素是不合适的。您可以直接重置它,而无需进行修改。但是,还有其他方法可以做到这一点。您可以尝试Immutability Helpers
您可以在this SO post上查看我在说什么的更多信息