在react

时间:2019-02-28 22:40:35

标签: reactjs

我目前正在从事的项目基本上是创建调查表,其中您可以针对每个问题有多个问题和多个答案。 单击一个按钮(一个添加一个新答案,一个添加一个新问题),在页面中添加了输入文本。

这是我的期望: https://i.imgur.com/XIWGEDK.png

会发生这种情况:https://i.imgur.com/WdLheeQ.png

Codepen示例:https://codepen.io/Sirius094/pen/aMdzeo

这是代码。我把它切了一些。下面有我的想法...

class FormQuiz extends React.Component<Props, State> {
    constructor(props: any) {
        super(props);
        this.state = {
            children: [],
            childrenAnswer: [],
            count: this.props.initialValues.question.length,
            prevState: 0,
            countAnswers: 0
        };
    }



    render() {

       return (
            <div>
              <div id="box-container">
               {this.state.children.map((input: any) => { return input; })}
              </div>

                <button type="button" onClick={() => {this.addQuestionForm();}}>
                    Add Question
                </button>
            </div>
        );
    }

    addQuestionForm() {
        var result = this.state.children;

        result.push(
            <div>
                <button type="button" onClick={() => 
                  this.addAnswer();
                 }}>
                    Add Answer
                </button>

                <FormikFieldWrapper
                    name={"question[" + 
                    this.state.count + "].text"}
                    value=""
                    type="text"
                />
            </div>
        );

        this.setState({
            children: result,
            prevState: this.state.count,
            count: this.state.count + 1
        });
    }

    addAnswer() {
        var result = this.state.children;

        result.push(
            <div>
                <FormikFieldWrapper
                    name={
                        "question[" +
                        this.state.prevState +
                        "].answers[" +
                        this.state.countAnswers +
                        "].text"
                    }
                    value=""
                    type="text"
                />
            </div>
        );

        this.setState({
            children: result,
            countAnswers: this.state.countAnswers + 1
        });
    }
}

因此,基本上,方法addQuestionForm()和addAnswer()使用数组子项来附加输入文本,我使用map()在render方法中对其进行迭代。 我认为问题在于数组子级实际上是一堆乱七八糟的输入,并且反应不知道在哪里正确呈现每个输入。还是我完全错了?我试图为答案(childAnswer)创建一个单独的数组,然后在其中一个内部使用了两个方法map(),但是失败了。我确定我确实缺少一些明显的东西...

2 个答案:

答案 0 :(得分:0)

React的优点在于,它允许您考虑声明式数据结构及其上的转换。如果您有问题,并且每个问题都可能有很多答案,那么似乎每个项目都包含一个集合的集合将是有意义的。像这样:

[
  {
    question: "Question A",
    answers: []
  },
  {
    question: "Question B",
    answers: []
  }
];

添加新问题将导致以下结果:

[
  {
    question: "Question A",
    answers: []
  },
  {
    question: "Question B",
    answers: []
  },
  {
    question: "Question C",
    answers: []
  }
];

为此添加答案,例如问题A:

[
  {
    question: "Question A",
    answers: ["Answer A"]
  },
  {
    question: "Question B",
    answers: []
  }
];

具有此数据结构,然后可以映射所有项目。在这种情况下,让我们假设不需要递归,因为我们完全可以肯定只有两个级别的嵌套集合。然后,代码将如下所示:

class FormQuiz extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {
      questions: [
        {
          question: "Question A",
          answers: ["Answer A"]
        },
        {
          question: "Question B",
          answers: []
        }
      ]
    };
  }

  render() {
    return this.state.questions.map(({ question, answers }) => (
      <React.Fragment>
        <div>
          <label>{question}</label>
          <input type="text" value={question} />
        </div>
        {answers.map(answer => (
          <div>
            <label>{answer}</label>
            <input type="text" value={answer} />
          </div>
        ))}
      </React.Fragment>
    ));
  }
}

鉴于此结构,您现在可以考虑消除缺少的键道具问题,通过组件状态实际更改值等。例如,添加一个新的(空)答案将是一个仅需要一个参数的函数:index(或更好的目标问题的全局唯一ID)。像这样:

class FormQuiz extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {
      questions: [
        {
          question: "Question A",
          answers: ["Answer A"]
        },
        {
          question: "Question B",
          answers: []
        }
      ]
    };
    this.appendAnswer = this.appendAnswer.bind(this);
  }

  appendEmptyAnswer(targetQuestionIndex) {
    this.setState(prevState => ({
      questions: prevState.questions.map((question, index) =>
        targetQuestionIndex !== index
          ? question
          : { ...question, answers: [...question.answers, ""] } // <- just add an empty string as the new answer
      )
    }));
  }

  render() {
    return this.state.questions.map(({ question, answers }, index) => (
      <React.Fragment>
        <div>
          <label>{question}</label>
          <input type="text" value={question} />
          <button onClick={() => this.appendEmptyAnswer(index)}>
            Add answer
          </button>
        </div>
        {answers.map(answer => (
          <div>
            <label>{answer}</label>
            <input type="text" value={answer} />
          </div>
        ))}
      </React.Fragment>
    ));
  }
}

以类似的方式添加问题。

一旦工作了,您可能会考虑对代码进行一些优化以提高可维护性。

另外,请记住,如果您有一个不会产生副作用的有状态组件,则钩子可以帮助使代码更具可读性,例如

const appendEmptyAnswer = (questions, targetQuestionIndex) =>
  questions.map((question, index) =>
    targetQuestionIndex !== index
      ? question
      : { ...question, answers: [...question.answers, ""] }
  );

const FormQuiz = () => {
  const [questions, setQuestions] = useState([
    {
      question: "Question A",
      answers: ["Answer A"]
    },
    {
      question: "Question B",
      answers: []
    }
  ]);

  return questions.map(({ question, answers }, index) => (
    <React.Fragment>
      <div>
        <label>{question}</label>
        <input type="text" value={question} />
        <button
          onClick={() => setQuestions(appendEmptyAnswer(questions, index))}
        >
          Add answer
        </button>
      </div>
      {answers.map(answer => (
        <div>
          <label>{answer}</label>
          <input type="text" value={answer} />
        </div>
      ))}
    </React.Fragment>
  ));
};

祝你好运!

答案 1 :(得分:0)

与@Fleischpfanzerl的答案一样,我建议您简化存储问题和答案的方式。在我的示例中,您将问题和答案都存储在一个数组中,并在添加新实体时呈现列表-这对于人类理解是显而易见的,并且(IMHO)避免了当您使用不同的实体时可能发生的问题和错误用于存储链接数据的数组:

class Button extends React.Component {
  state = {
    questions: [],
  };

  renderQuestions() {
    return this.state.questions.map((question, questionId) => (
      <div>
        <div>
          <label>Question #{question.id}</label>
          <input type="text" />
          <button type="button" onClick={() => {this.addAnswer(questionId)}}>
            Add Answer 
          </button>
        </div>
        <div>
          {this.renderAnswers(questionId)}
        </div>
      </div>
    ));
  }

  renderAnswers(questionId) {
    return this.state.questions[questionId].answers.map((answer) => (
      <div>
        <label>Answer #{answer.id}</label>
        <input type="text" />
      </div>
    ));
  }

  render() {
    return (
      <div>
        <div id="box-container">
          {this.renderQuestions()}
        </div>
        <br/>
        <button type="button" onClick={() => {this.addQuestionForm()}}>
          Add Question
        </button>
      </div>
    );
  }


  addQuestionForm() {
    const { questions } = this.state;

    this.setState({
      questions: [
        ...questions,
        {
          id: questions.length,
          answers: [],
        },
      ],
    });
  }

  addAnswer(questionId) {
    const questionsCloned = JSON.parse(JSON.stringify(this.state.questions));

    questionsCloned[questionId].answers.push({
      id: questionsCloned[questionId].answers.length,
      text: "",
    });

    this.setState({
      questions: questionsCloned,
    });
  }
}

React.render(<Button />, document.getElementById('app'));

所以,数组的结构是这样的:

questions: [
  {
    id: 1,
    answers: [
      {
        id: 1,
      },
      {
        id: 2,
      },
    },
  },
  {
    id: 2,
    answers: [
      {
        id: 1,
      },
      {
        id: 2,
      },
      {
        id: 3,
      },
    },
  },
  ...
]

Codepen:https://codepen.io/anon/pen/GeoJPG?editors=1011