React.js在映射表单上设置状态

时间:2018-12-14 14:20:24

标签: reactjs

我是一名新手,我正在准备一个看起来像这样的表格:左边是实际的输入字段,右边是用户正在键入内容的实时预览。

表单名称及其用途有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}
            />
          ))}

我在做什么错? 预先感谢您的帮助!

1 个答案:

答案 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上查看我在说什么的更多信息