javascript React删除应答处理程序

whhtz7ly  于 2023-09-29  发布在  Java
关注(0)|答案(2)|浏览(60)

我正在删除我按下的删除按钮旁边的答案。我正在生成一个答案列表,每个答案旁边都有一个相应的删除按钮。然而,当我按下删除按钮时,最后两个答案总是被删除。我也试过使用拼接方法,但结果是一样的。也许我传递的参数有问题,但我现在真的不确定。
编辑:我发现这部分代码中的处理程序被执行了两次,但是,我找不到这种行为的原因:

{poll.questions.map((questionObj, questionIndex) => (
        <div key={questionIndex}>
          <h2>
            {questionObj.question +
              (questionObj.multiChoice
                ? " - (multiple choice)"
                : " - (single choice)")}
            <br />
            <button onClick={() => handleDeleteQuestion(questionIndex)}>
              Delete question
            </button>
          </h2>
          {questionObj.choices.map((choice, choiceIndex) => (
            <div key={choiceIndex}>
              <input
                type="text"
                placeholder={`Choice ${choiceIndex + 1}`}
                value={choice.text}
                onChange={(e) =>
                  handleAnswerChange(questionIndex, choiceIndex, e.target.value)
                }
              />
            </div>
          ))}
          <button
            onClick={(e) => {
              e.stopPropagation(); // Prevent event propagation
              handleDeleteAnswer(questionIndex);
            }}
          >
            -
          </button>
        </div>
      ))}

完整代码:

import React, { useState } from "react";

function NewPoll() {
  const [poll, setPoll] = useState({
    name: "",
    description: "",
    multiChoice: false,
    questions: [],
    answers: [],
  });

  const handleInputChange = (e) => {
    const { name, value, type, checked } = e.target;
    const newValue = type === "checkbox" ? checked : value;

    setPoll((prevPoll) => ({
      ...prevPoll,
      [name]: newValue,
    }));
  };

  const handleMultipleAnswer = () => {
    const question = prompt("Enter the multiple-choice question:");

    if (question !== null) {
      const numAnswers = parseInt(
        prompt("Enter the number of possible answers:"),
        10
      );

      if (!isNaN(numAnswers)) {
        const newQuestion = {
          question,
          choices: Array(numAnswers).fill(""), // Initialize with empty strings
          multiChoice: true,
        };

        setPoll((prevPoll) => ({
          ...prevPoll,
          questions: [...prevPoll.questions, newQuestion],
        }));
      }
    }
  };

  const handleSingleAnswer = () => {
    const question = prompt("Enter the single choice question:");

    if (question !== null) {
      const numAnswers = parseInt(
        prompt("Enter the number of possible answers:"),
        10
      );

      if (!isNaN(numAnswers)) {
        const newQuestion = {
          question,
          choices: Array(numAnswers).fill(""), // Initialize with empty strings
          multiChoice: false,
        };

        setPoll((prevPoll) => ({
          ...prevPoll,
          questions: [...prevPoll.questions, newQuestion],
        }));
      }
    }
  };

  /*  const [checkedState, setCheckedState] = useState(
    new Array(poll.questions.length).fill(false)
  );
const handleChoiceChange = (position) => {
    const updatedCheckedState = checkedState.map((item, choiceIndex) =>
    choiceIndex === position ? !item : item
    );

    setCheckedState(updatedCheckedState);
  };

  */

  const handleAnswerChange = (questionIndex, choiceIndex, newText) => {
    setPoll((prevPoll) => {
      const updatedQuestions = [...prevPoll.questions];
      updatedQuestions[questionIndex].choices[choiceIndex] = newText;
      return {
        ...prevPoll,
        questions: updatedQuestions,
      };
    });
  };

  const handleDeleteQuestion = (questionIndex) => {
    setPoll((prevPoll) => {
      const updatedQuestions = [...prevPoll.questions];
      const deletedQuestion = updatedQuestions.splice(questionIndex, 1)[0];

      deletedQuestion.choices = [];

      return {
        ...prevPoll,
        questions: updatedQuestions,
      };
    });
  };

  const handleDeleteAnswer = (questionIndex) => {
    setPoll((prevPoll) => {
      const updatedQuestions = [...prevPoll.questions];
      const lastQuestion = updatedQuestions[questionIndex];

      if (lastQuestion.choices.length > 0) {
        lastQuestion.choices.pop(); // Remove the last choice
      }

      console.log("hey");

      return {
        ...prevPoll,
        questions: updatedQuestions,
      };
    });
  };

  return (
    <>
      <h1>Enter poll name:</h1>
      <br />
      <input
        type="text"
        name="name"
        value={poll.name}
        onChange={handleInputChange}
      />
      <h1>Enter a description:</h1>
      <textarea
        rows="10"
        cols="50"
        name="description"
        value={poll.description}
        onChange={handleInputChange}
      ></textarea>{" "}
      <br />
      <button onClick={handleMultipleAnswer}>
        Create a question with multiple answers
      </button>
      <button onClick={handleSingleAnswer}>
        Create a question with a single answer
      </button>
      {poll.questions.map((questionObj, questionIndex) => (
        <div key={questionIndex}>
          <h2>
            {questionObj.question +
              (questionObj.multiChoice
                ? " - (multiple choice)"
                : " - (single choice)")}
            <br />
            <button onClick={() => handleDeleteQuestion(questionIndex)}>
              Delete question
            </button>
          </h2>
          {questionObj.choices.map((choice, choiceIndex) => (
            <div key={choiceIndex}>
              <input
                type="text"
                placeholder={`Choice ${choiceIndex + 1}`}
                value={choice.text}
                onChange={(e) =>
                  handleAnswerChange(questionIndex, choiceIndex, e.target.value)
                }
              />
            </div>
          ))}
          <button
            onClick={(e) => {
              e.stopPropagation(); // Prevent event propagation
              handleDeleteAnswer(questionIndex);
            }}
          >
            -
          </button>
        </div>
      ))}
      <br />
      <button> Create a new poll </button>
    </>
  );
}

export default NewPoll;

这是我到目前为止试过的代码

yrdbyhpb

yrdbyhpb1#

您的handleDeleteAnswer触发了一次,但您的setPoll回调触发了两次。您可以通过在handleDeleteAnswer的最开始放置一个console.log,并在setPoll回调的最开始放置另一个console.log来验证这一点。我相信这是由于在开发中使用了React.StrictMode。参见https://github.com/facebook/react/issues/12856
为了解决这个问题,我建议为你的每个问题和选择分配唯一的ID,然后使用这些ID来确定你想要删除的项目,而不是使用索引。即使setPoll调用触发了两次,第一次删除的ID在第二次时也将不再存在-它是幂等的。
举一个简单的例子,我在你的代码中修改并更新了它,这样在你初始化choices数组的地方,我现在把它Map到一个包含ID的对象:

// choices: Array(numAnswers).fill("")
choices: Array(numAnswers).fill("").map((choice, i) => ({id: i, choice}))

在呈现您的选择时,将choice.id传入handleDeleteAnswer函数,而不是选择的索引。

{questionObj.choices.map((choice, choiceIndex) => (
  <div key={choice.id}>
    <input
      type="text"
      placeholder={`Choice ${choiceIndex + 1}`}
      value={choice.text}
      onChange={(e) =>
        handleAnswerChange(questionIndex, choice.id, e.target.value)
      }
    />
    <button
      onClick={() => handleDeleteAnswer(questionIndex, choice.id)}
    >
      Delete
    </button>
  </div>
))}

然后更新handleDeleteAnswer函数,使用choiceId来定位要删除的选项:

const handleDeleteAnswer = (questionIndex, choiceId) => {
  setPoll((prevPoll) => {
    console.log(prevPoll);
    const updatedQuestions = [...prevPoll.questions];
    updatedQuestions[questionIndex].choices = updatedQuestions[
      questionIndex
    ].choices.filter((choice) => choice.id !== choiceId);
    return {
      ...prevPoll,
      questions: updatedQuestions,
    };
  });
};

顺便说一下,在这种情况下,应该避免使用数组索引作为元素键;指数可能会发生变化。这将是使用ID的另一个候选者。

zpf6vheq

zpf6vheq2#

嗨,我想你的问题是你有10个盒子和1个按钮来关闭盒子,或者你有10个盒子和10个关闭/删除按钮?
如果你有10个盒子,并且在每个盒子里都有一个删除按钮,你可以使用“closet”,Closest将查找父元素作为选择器。这里是一个例子。

cnst clickHandler = (e) => {
   e.preventDefault()
   e.target.closest("#card") // e.target.closest("[key='123']")
}

<main>
  <section id="card">
     <div id="card">
       <button onClick={(e) => clickHandler(e)}>
          This button will find his parent div
          as closest parent with id card
       </button>
     </div>
     <div id="card">
       <button>
         This button will find his parent div
         as closest parent with id card
       </button>
     </div>
     <div>
       <button>
         This button will find the section
         as closest parent with the id card!
       </button>
     </div>
  <section>
</main>

否则,你也可以使用'事件委托',如果你问谷歌或最接近你会发现很多例子,也许这将有助于你找到最好的方法^^
我希望我没有错过这个问题XD

相关问题