繁体   English   中英

不需要的React组件重新渲染?

[英]Unwanted React Component rerender?

因此,这是一种表单,用户可以在其中添加部分以添加问题(以构建测验),并且我注意到当我填写“ Answer Choices并将文件放置到我的dropZone (drop可以正常运行,但无法正确更新)可以忽略这一点),然后dropZone Answer ChoicesdropZone ,字段(如refresh)变为空。

我不完全确定为什么会这样,我曾尝试研究类似的问题,但无法使其正常工作。 这是我的CodeSandbox和我的应用程序。

我认为这可能是我的Questions组件中的addQuestion函数。 这是该代码:

  addQuestion = question => {
    questionIdx++;
    var newQuestion = { uniqueId: uuid(), question: "" }
    this.setState(prevState => ({
      questions: [...prevState.questions, newQuestion]
    }));
    return { questions: newQuestion }
  };

我是React和Js的新手,所以任何技巧/解释都可以帮助您。 谢谢!

当您添加新问题并更新Questions组件状态变量questions (数组类型)时,整个组件( Questions及其子元素)将经历一个更新过程,在此过程中,新组件将基于新问题重新计算输出DOM树(虚拟DOM)。状态并将其与状态更改前的虚拟DOM进行比较。 在“渲染”任何东西之前,它会检查两个版本的虚拟DOM是否彼此不同,如果是,它将尽可能有效地重新渲染那些变化的部分。

在您的情况下,重新计算会在许多Question看到许多Answers组件,并且由于Answers没有任何道具,因此它基本上是到其初始状态的全新渲染,其中包括4个空输入。 我能想到的最简单的解决方案是在Questions组件的状态下,确保this.state.questions数组中的每个对象都有一个answers属性(对象数组类型)。 addQuestion方法中,修改var newQuestion = { uniqueId: uuid(), question: "" }; 将此数据包含在与该问题相关的答案上。

然后渲染每个单独的问题时,通过此答案数据(应答的阵列)为道具来Answers并依次对每个单独的Answer基于索引(一个对象或一个字符串)组件。 与此同时,你必须通过updateAnswers方法作为道具Question ,反过来传递给AnswersAnswer ,当被称为Answer的输入字段改变。 需要将问题ID和答案ID传递给此方法,以最终修改现在应存储在Questions组件状态下的答案数据。 我调整了以下沙箱中的代码以遵循以下原则,尽管我不确定要清除所有损坏:

import React, { Component } from "react";
import "./App.css";

var uuid = require("uuid-v4");
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true

class DropZone extends Component {
  constructor(props) {
    super(props);
    this.state = {
      file: "",
      fileId: uuid(),
      className: "dropZone"
    };
    this.handleChange = this.handleChange.bind(this);
    this._onDragEnter = this._onDragEnter.bind(this);
    this._onDragLeave = this._onDragLeave.bind(this);
    this._onDragOver = this._onDragOver.bind(this);
    this._onDrop = this._onDrop.bind(this);
  }

  handleChange(file = "") {
    this.setState({
      file: URL.createObjectURL(file)
    });
    //document.getElementsByClassName("dropZone").style.backgroundImage = 'url(' + this.state.file + ')';
  }

  componentDidMount() {
    window.addEventListener("mouseup", this._onDragLeave);
    window.addEventListener("dragenter", this._onDragEnter);
    window.addEventListener("dragover", this._onDragOver);
    document
      .getElementById("dragbox")
      .addEventListener("dragleave", this._onDragLeave);
    window.addEventListener("drop", this._onDrop);
  }

  componentWillUnmount() {
    window.removeEventListener("mouseup", this._onDragLeave);
    window.removeEventListener("dragenter", this._onDragEnter);
    window.addEventListener("dragover", this._onDragOver);
    document
      .getElementById("dragbox")
      .removeEventListener("dragleave", this._onDragLeave);
    window.removeEventListener("drop", this._onDrop);
  }

  _onDragEnter(e) {
    e.stopPropagation();
    e.preventDefault();
    return false;
  }

  _onDragOver(e) {
    e.preventDefault();
    e.stopPropagation();
    return false;
  }

  _onDragLeave(e) {
    e.stopPropagation();
    e.preventDefault();
    return false;
  }

  _onDrop(e, event) {
    e.preventDefault();
    this.handleChange(e.dataTransfer.files[0]);
    let files = e.dataTransfer.files;
    console.log("Files dropped: ", files);
    // Upload files
    console.log(this.state.file);
    return false;
  }

  render() {
    const uniqueId = this.state.fileId;
    return (
      <div>
        <input
          type="file"
          id={uniqueId}
          name={uniqueId}
          class="inputFile"
          onChange={e => this.handleChange(e.target.files[0])}
        />
        <label htmlFor={uniqueId} value={this.state.file}>
          {this.props.children}
          <div className="dropZone" id="dragbox" onChange={this.handleChange}>
            Drop or Choose File
            <img src={this.state.file} id="pic" name="file" accept="image/*" />
          </div>
        </label>
        <div />
      </div>
    );
  }
}

class Answers extends Component {
  constructor(props) {
    super(props);
    this.state = {
      answers: props.answers,
    };
    this.handleUpdate = this.handleUpdate.bind(this);
  }

  // let event = {
  //   index: 1,
  //   value: 'hello'
  // };
  handleUpdate(event) {
    //if ("1" == 1) // true
    //if ("1" === 1) //false
    // var answers = this.state.answers;
    // answers[event.index] = event.value;
    // this.setState(() => ({
    //   answers: answers
    // }));

    var answers = this.state.answers.slice();

    for (var i = 0; i < answers.length; i++) {
      if (answers[i].answerId == event.answerId) {
        answers[i].answer = event.value;
        break;
      }
    }
    this.setState(() => ({
      answers: answers
    }));
    this.props.updateAnswers(answers)

    console.log(event);
  }

  render() {
    return (
      <div id="answers">
        Answer Choices<br />
        {this.state.answers.map((value, index) => (
          <Answer
            key={`${value}-${index}`}
            onUpdate={this.handleUpdate}
            value={value}
            number={index}
            name="answer"
          />
        ))}
      </div>
    );
  }
}

class Answer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      answer: props.value.answer,
      answerId: props.value.answerId,
      isCorrect: props.value.isCorrect,
    };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    const target = event.target;
    const value = target.type === "checkbox" ? target.checked : target.value;
    this.setState({
      answer: value
    });
    this.props.onUpdate({
      answerId: this.state.answerId,
      value
    });

    // let sample = {
    //   kyle: "toast",
    //   cam: "pine"
    // };

    // sample.kyle
    // sample.cam
  }
  render() {
    return (
      <div>
        <input type="checkbox" />
        <input
          type="text"
          value={this.state.answer}
          onChange={this.handleChange}
          key={this.state.answerId}
          name="answer"
        />
        {/*console.log(this.state.answerId)*/}
      </div>
    );
  }
}

var questionIdx = 0;

class Questions extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questions: []
    };
    this.handleUpdate = this.handleUpdate.bind(this);
    this.handleUpdate = this.handleUpdate.bind(this);
    this.removeQuestion = this.removeQuestion.bind(this);
  }

  handleUpdate(event) {
    //if ("1" == 1) // true
    //if ("1" === 1) //false
    var questions = this.state.questions.slice();

    for (var i = 0; i < questions.length; i++) {
      if (questions[i].uniqueId == event.uniqueId) {
        questions[i].question = event.value;
        break;
      }
    }
    this.setState(() => ({
      questions: questions
    }));

    console.log(event, questions);
  }

  updateAnswers(answers, uniqueId) {
    const questions = this.state.questions
    questions.forEach((question) => {
      if (question.uniqueId === uniqueId) {
        question.answers = answers
      }
    })
    this.setState({
      questions,
    })
  }

  addQuestion = question => {
    questionIdx++;
    var newQuestion = { 
      uniqueId: uuid(),
      question: "",
      answers: [
        { answer: "", answerId: uuid(), isCorrect: false,},
        { answer: "", answerId: uuid(), isCorrect: false,},
        { answer: "", answerId: uuid(), isCorrect: false,},
        { answer: "", answerId: uuid(), isCorrect: false,}]
      }
    this.setState(prevState => ({
      questions: [...prevState.questions, newQuestion]
    }));
    return { questions: newQuestion };
  };

  removeQuestion(uniqueId, questions) {
    this.setState(({ questions }) => {
      var questionRemoved = this.state.questions.filter(
        props => props.uniqueId !== uniqueId
      );
      return { questions: questionRemoved };
    });
    console.log(
      "remove button",
      uniqueId,
      JSON.stringify(this.state.questions, null, " ")
    );
  }

  render() {
    return (
      <div id="questions">
        <ol id="quesitonsList">
          {this.state.questions.map((value, index) => (
            <li key={value.uniqueId}>
              {
                <RemoveQuestionButton
                  onClick={this.removeQuestion}
                  value={value.uniqueId}
                />
              }
              {
                <Question
                  onUpdate={this.handleUpdate}
                  value={value}
                  number={index}
                  updateAnswers={(answers) => this.updateAnswers(answers, value.uniqueId) }
                />
              }
              {<br />}
            </li>
          ))}
        </ol>
        <AddQuestionButton onClick={this.addQuestion} />
      </div>
    );
  }
}

class Question extends Component {
  constructor(props) {
    super(props);
    this.state = {
      question: props.value.question,
      uniqueId: props.value.uniqueId,
      answers: props.value.answers,
    };
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange(event) {
    const target = event.target;
    const value = target.type === "checkbox" ? target.checked : target.value;
    this.setState({
      question: value
    });
    this.props.onUpdate({
      uniqueId: this.state.uniqueId,
      value
    });
  }

  render() {
    return (
      <div id={"questionDiv" + questionIdx} key={myUUID + questionIdx + 1}>
        Question<br />
        <input
          type="text"
          value={this.state.question}
          onChange={this.handleChange}
          key={this.state.uniqueId}
          name="question"
        />
        <DropZone />
        <Answers updateAnswers={this.props.updateAnswers} answers={this.state.answers} />
      </div>
    );
  }
}

class IntroFields extends Component {
  constructor(props) {
    super(props);
    this.state = {
      title: "",
      author: ""
    };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    const target = event.target;
    const value = target.type === "checkbox" ? target.checked : target.value;
    const name = target.name;
    console.log([name]);
    this.setState((previousState, props) => ({
      [name]: value
    }));
  }

  render() {
    return (
      <div id="IntroFields">
        Title:{" "}
        <input
          type="text"
          value={this.state.title}
          onChange={this.handleChange}
          name="title"
        />
        Author:{" "}
        <input
          type="text"
          value={this.state.author}
          onChange={this.handleChange}
          name="author"
        />
      </div>
    );
  }
}

class AddQuestionButton extends Component {
  addQuestion = () => {
    this.props.onClick();
  };

  render() {
    return (
      <div id="addQuestionButtonDiv">
        <button id="button" onClick={this.addQuestion} />
        <label id="addQuestionButton" onClick={this.addQuestion}>
          Add Question
        </label>
      </div>
    );
  }
}

class RemoveQuestionButton extends Component {
  removeQuestion = () => {
    this.props.onClick(this.props.value);
  };

  render() {
    return (
      <div id="removeQuestionButtonDiv">
        <button id="button" onClick={this.removeQuestion} key={uuid()} />
        <label
          id="removeQuestionButton"
          onClick={this.removeQuestion}
          key={uuid()}
        >
          Remove Question
        </label>
      </div>
    );
  }
}

class BuilderForm extends Component {
  render() {
    return (
      <div id="formDiv">
        <IntroFields />
        <Questions />
      </div>
    );
  }
}
export default BuilderForm;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM