You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React删除答案按钮异常:点击后误删最后两个答案的问题排查

问题根源与修复方案

核心问题分析

  1. Strict Mode 触发重复执行
    React严格模式在开发环境中会重复调用setState相关逻辑,你的handleDeleteAnswer原本每次执行都会pop()一个选项,两次执行就会删掉两个答案。这是开发环境的检测特性,生产环境不会出现,但逻辑本身存在缺陷。

  2. 数据结构不匹配
    初始化问题时,choices是字符串数组,但渲染输入框时用了value={choice.text}——字符串没有text属性,导致输入框始终为undefined,触发不必要的重渲染,加重问题。

  3. 删除按钮逻辑与需求不符
    你期望点击对应答案旁的按钮删除该答案,但当前删除按钮放在所有答案外侧,且逻辑是删除当前问题的最后一个答案,完全不符合需求。


分步修复

1. 统一数据结构,将choices改为对象数组

初始化问题时,把choices改成包含text字段的对象数组,匹配渲染时的取值逻辑:

// 在handleMultipleAnswer和handleSingleAnswer中替换初始化代码
choices: Array(numAnswers).fill(null).map(() => ({ text: "" })),

2. 调整删除按钮位置,传递对应选项索引

把删除按钮放到每个选项的容器内,同时传递该选项的索引给删除函数:

{questionObj.choices.map((choice, choiceIndex) => (
  <div key={choiceIndex} style={{ display: "flex", gap: "8px", margin: "4px 0" }}>
    <input
      type="text"
      placeholder={`Choice ${choiceIndex + 1}`}
      value={choice.text}
      onChange={(e) =>
        handleAnswerChange(questionIndex, choiceIndex, e.target.value)
      }
    />
    {/* 每个选项旁添加删除按钮 */}
    <button onClick={() => handleDeleteAnswer(questionIndex, choiceIndex)}>
      -
    </button>
  </div>
))}

3. 重写删除逻辑,精准删除指定选项

修改handleDeleteAnswer,通过splice删除对应索引的选项,避免操作原数组:

const handleDeleteAnswer = (questionIndex, choiceIndex) => {
  setPoll((prevPoll) => {
    const updatedQuestions = [...prevPoll.questions];
    // 深拷贝当前问题的选项数组
    const updatedChoices = [...updatedQuestions[questionIndex].choices];
    updatedChoices.splice(choiceIndex, 1);
    // 更新问题的选项列表
    updatedQuestions[questionIndex] = {
      ...updatedQuestions[questionIndex],
      choices: updatedChoices
    };
    return {
      ...prevPoll,
      questions: updatedQuestions,
    };
  });
};

4. 修正输入框更新逻辑(可选)

为避免直接修改状态对象,更新handleAnswerChange为纯函数式修改:

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

完整修复后的组件代码

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(null).map(() => ({ text: "" })),
          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(null).map(() => ({ text: "" })),
          multiChoice: false,
        };

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

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

  const handleDeleteQuestion = (questionIndex) => {
    setPoll((prevPoll) => {
      const updatedQuestions = [...prevPoll.questions];
      updatedQuestions.splice(questionIndex, 1);
      return {
        ...prevPoll,
        questions: updatedQuestions,
      };
    });
  };

  const handleDeleteAnswer = (questionIndex, choiceIndex) => {
    setPoll((prevPoll) => {
      const updatedQuestions = [...prevPoll.questions];
      const updatedChoices = [...updatedQuestions[questionIndex].choices];
      updatedChoices.splice(choiceIndex, 1);
      updatedQuestions[questionIndex] = {
        ...updatedQuestions[questionIndex],
        choices: updatedChoices
      };
      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} style={{ margin: "16px 0" }}>
          <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} style={{ display: "flex", gap: "8px", margin: "4px 0" }}>
              <input
                type="text"
                placeholder={`Choice ${choiceIndex + 1}`}
                value={choice.text}
                onChange={(e) =>
                  handleAnswerChange(questionIndex, choiceIndex, e.target.value)
                }
              />
              <button onClick={() => handleDeleteAnswer(questionIndex, choiceIndex)}>
                -
              </button>
            </div>
          ))}
        </div>
      ))}
      <br />
      <button> Create a new poll </button>
    </>
  );
}

export default NewPoll;

内容的提问来源于stack exchange,提问作者Iyexis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:10:56