簡體   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