skip to Main Content

I’ve created a survey app in react, and up until now I’ve wrote for each question the code for each radiobutton/input to asnwer. The problem is that I have survey with a lot of question and using this method create a huge code. So my idea was to write the question/answer in json format and then showing them by iterating over them and then create and if statament to show the code for radiobutton or input based on the answerType. I’ve menaged to display the question, but I’m having trouble showing the radiobutton or input for each question and I’m getting this error:

question.map is not a function
TypeError: question.map is not a function  

I also need to keep track of each answer. in my previous code I did it like this

  const [answers, setAnswers] = useState({
    answerOne: "",
    answerTwo: "",
  });

  const handleChange = (e) => {
    setAnswers((prev) => ({ ...prev, [e.target.name]: e.target.value }));
  };

And then adding

onChange={handleChange}

to each input, but right now I don’t know how to do it. This is my code right now:

import React, { useState } from "react";
import "./survey.css";

const MapSurvey = () => {
  const questions = [
    {
      questionText: "First Q",
      answerOptions: [
        { answerText: "One", value: 1 },
        { answerText: "Two", value: 2 },
        { answerText: "Three", value: 3 },
        { answerText: "Four", value: 4 },
        { answerText: "Five", value: 5 },
      ],
      answerType: "radio",
    },
    {
      questionText: "Second",
      answerOptions: [
        { answerText: "One", value: 1 },
        { answerText: "Two", value: 2 },
        { answerText: "Three", value: 3 },
        { answerText: "Four", value: 4 },
      ],
      answerType: "radio",
    },
    {
      questionText: "Third",
      answerOptions: [
        { answerText: "One", value: 1 },
        { answerText: "Two", value: 2 },
        { answerText: "Three", value: 3 },
        { answerText: "Four", value: 4 },
        { answerText: "Five", value: 5 },
      ],
      answerType: "radio",
    },
    {
      questionText: "FirstInput",
      answerType: "text",
    },
  ];
  return (
    <div className="w-full py-[2rem] px-4 bg-white">
      <div className="mx-auto text-center flex flex-col">
        <form>
          {questions.map((question, i) => (
            <div className="flex flex-col p-4 my-4">
              <h2>{question.questionText}</h2>

              {question.map((answ) => (
                <div className="radio mt-3 mb-3">
                  <input
                    name="answerOne"
                    type="radio"
                    id="11"
                    hidden="hidden"
                    value="1"
                  />
                  <label
                    htmlFor="11"
                    className="px-2 py-1 rounded-lg flex justify-left items-center font-medium text-black checked:text-white w-full sm:w-6/12 h-10"
                  >
                    {answ.answerText}
                  </label>
                </div>
              ))}
            </div>
          ))}
        </form>

        <button>Submit</button>
      </div>
    </div>
  );
};

export default MapSurvey;

2

Answers


  1. question is an object inside questions, not an array itself, so you don’t need to map over each question. Use the properties you’re interested in directly: {questionText}, {question.answerOptions.map(answ => ...}.

    Login or Signup to reply.
  2. When you map over all questions everytime you will get one of below two question object as per your data-set.

      question = {
          questionText: "Second",
          answerOptions: [
            { answerText: "One", value: 1 },
            { answerText: "Two", value: 2 },
            { answerText: "Three", value: 3 },
            { answerText: "Four", value: 4 },
          ],
          answerType: "radio",
        }
    
    

    OR

      question = {
          questionText: "Third Question",
          answerType: "text",
        }
    
    

    So you need to check if question’s answerType is radio or text.
    accordingly you need to put condition for this.

    (Note*

    1. It will be better to have a separate component returning rendered question and options when you pass this question object data as prop in case of multiple answer types.
    2. Always provide key to html element that you are rendering inside map for any other loop. It will help React differentiate and distinguish elements from each other, increasing its performance when diffing between the virtual and real DOM.
      )

    So your updated component something like this.

    
    
    import React, { useState } from "react";
    
    const MapSurvey = () => {
      const [answers, setAnswers] = useState({});
    
      const handleChange = e => {
        setAnswers(prev => ({ ...prev, [e.target.name]: e.target.value }));
        setTimeout(() => console.log(answers), 1000);
      };
    
      const questions = [
        {
          questionText: "First Q",
          answerOptions: [
            { answerText: "One", value: 1 },
            { answerText: "Two", value: 2 },
            { answerText: "Three", value: 3 },
            { answerText: "Four", value: 4 },
            { answerText: "Five", value: 5 }
          ],
          answerType: "radio"
        },
        {
          questionText: "Second",
          answerOptions: [
            { answerText: "One", value: 1 },
            { answerText: "Two", value: 2 },
            { answerText: "Three", value: 3 },
            { answerText: "Four", value: 4 }
          ],
          answerType: "radio"
        },
        {
          questionText: "Third",
          answerOptions: [
            { answerText: "One", value: 1 },
            { answerText: "Two", value: 2 },
            { answerText: "Three", value: 3 },
            { answerText: "Four", value: 4 },
            { answerText: "Five", value: 5 }
          ],
          answerType: "radio"
        },
        {
          questionText: "FirstInput",
          answerType: "text"
        }
      ];
      return (
        <div className="w-full py-[2rem] px-4 bg-white">
          <div>
            <form>
              {questions.map((question, idx) => (
                <div key={`question-no-${idx}`} className="flex flex-col p-4 my-4">
                  <span className="text-lg font-bold">{question.questionText}</span>
    
                  {question.answerType === "radio" ? (
                    <div className="flex gap-2">
                      {question.answerOptions.map((option, optionIdx) => (
                        <div key={`question_${idx}_option_${optionIdx}`} className="mt-3 mb-3">
                          <input
                            name={`question_${idx}`}
                            type={question.answerType}
                            id={`question_${optionIdx}`}
                            onChange={handleChange}
                            value={option.value}
                          />
                          <label
                            htmlFor={`question_${idx}`}
                            className="px-2 py-1 rounded-lg flex justify-left items-center font-medium text-black checked:text-white w-full sm:w-6/12 h-10"
                          >
                            {option.answerText}
                          </label>
                        </div>
                      ))}
                    </div>
                  ) : (
                    <>
                      <input
                        name={`question_${idx}`}
                        type={question.answerType}
                        id={`question_${idx}`}
                        onChange={handleChange}
                      />
                    </>
                  )}
                </div>
              ))}
            </form>
    
            <button>Submit</button>
          </div>
        </div>
      );
    };
    
    export default MapSurvey;
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search