0

I am just messing around in React as a beginner. I am trying to make a questionnaire. I ask the user whether they have a laptop, and then continue asking questions about it. If the 'yes' button is clicked on the first question, the next question is displayed - what kind of laptop. Here I want to display 3 options in the form of buttons instead of 2 like the previous question and the chain continues. If 'no' is clicked on the first question, another question is displayed. I want these questions to be on the same webpage and not redirected to another page. I also want the upcoming question buttons to not be displayed initially itself. I have a good understanding of HTML and JS and basic level fundamentals of React.

class App extends React.Component {

  constructor() {
    super(); 
    this.state = { showMessage: false }
  }

  _showMessage = (bool) => {
    this.setState({
      showMessage: bool
    });
  }

  render() {
    return (
      <div>
        Do you have a laptop <br/>
        <button onClick={this._showMessage.bind(null, true)}>yes</button>
        <button onClick={this._showMessage.bind(null, false)}>no</button>
        { this.state.showMessage && (<div>What kind of laptop?</div>) }
        <br/>
{/* I want to hide these buttons until the 'yes' button is clicked*/}
        <button onClick={this._showMessage.bind(null, true)}>Windows</button>
        <button onClick={this._showMessage.bind(null, false)}>IOS</button>
{/* I want to display a third button which says none of the above */}
        { this.state.showMessage && (<div>when did you buy it?</div>) }
    </div> )
  }
}
1
  • There are many ways to do this. To keep code clean, I'd suggest making a component for each question /section. Track the step and the answer in state so that you can go back to previous answers. Then render the question based on the step if step === x then return y Commented Jan 7, 2023 at 10:58

4 Answers 4

1

I'm not familiar with class based components but here is how I would go about creating a questionnaire. Note that I am using functional components.

First thing would be to create an object containing all of your questions, answers and possible outcomes for each step. Here is a quick example :

export const QuestionsAnswers = {
  1: {
    question: "Do you have a laptop",
    answers: [
      { answer: "Yes", nextStep: 2 },
      { answer: "No", nextStep: 3 },
    ],
  },
  2: {
    question: "What kind of laptop?",
    answers: [
      { answer: "Windows", nextStep: 5 },
      { answer: "IOS", nextStep: 5 },
    ],
  },
  3: {
    question: "Do you have a desktop?",
    answers: [
      { answer: "Yes", nextStep: 4 },
      { answer: "No", nextStep: 5 },
    ],
  },
  4: {
    question: "What OS does it have?",
    answers: [
      { answer: "Windows", nextStep: 5 },
      { answer: "IOS", nextStep: 5 },
    ],
  },
  5: {
    question: "Questions Over!",
    answers: [],
  },
};

Next would be to create a component that takes a step from your questions and answers object as props. This component will return the question text as well as loop over the options for the user to select. It will also take the setState function to set the next step :

const Question = ({ questions, setStep }) => {
  return (
    <div>
      <p>{questions.question}</p>
      {questions.answers.map((e) => {
        return <button onClick={() => setStep(e.nextStep)}>{e.answer}</button>;
      })}
    </div>
  );
};
export default Question;

Lastly, in the parent component I would declare the step state and render out the Question component. :

import "./styles.css";
import React from "react";
import Question from "./Question";
import { QuestionsAnswers } from "./QuestionsAnswers";

export default function App() {
  const [step, setStep] = React.useState(1);
  return (
    <div className="App">
      <h1>Question</h1>
      <Question questions={QuestionsAnswers[step]} setStep={setStep} />
    </div>
  );
}

With this sort of setup, you can easily extend / adjust your questions and your code will dynamically render what you need just based off your initial questions and answers object.

A codesandbox example : https://codesandbox.io/s/fast-field-vyqlr1?file=/src/QuestionsAnswers.js:0-690

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your answer, but I do not want the previous question to disappear and rather want the chain of questions to be displayed together.
I updated the sandbox with a quick example for you. Basically what it's doing is tracking the users answers in state and showing the answers above the new questions. Following the same logic as previously of creating a component for the questions. I have not personally built a questionnaire and I'm sure there are probably better ways to go about it. But if this is for practice, practicing breaking down your app into components is the best way to go :)
Thank you for your answer. I was also trying to add another functionality when the windows button is clicked which would allow me to input the versions and other details. I was wondering how to do that. Is there an if statement in react that I can use such that if useranswer = 'windows' then another function follows which would ask me what version is your windows and I would enter the version manually? rather than another quetion which would follow as soon as windows button is clicked. I am just trying to learn react hooks better.
The wonderful thing about react is that there are tons of libraries already built that you can use. If you find yourself re inventing the wheel, check out npmjs for some options . like this one npmjs.com/package/survey-core
1

You can store the current step in the state, increase it each time you click on the desired button and, depending on the step, display the necessary content on the page.

const App = () => {
      const [step, setStep] = React.useState(1)

      const handleNextStep = () => setStep(prevStep => prevStep + 1)

      const handleFirstStep = () => setStep(1)

      return (
        <div>
          Do you have a laptop <br/>
          <button onClick={handleNextStep}>yes</button>
          <button onClick={handleFirstStep}>no</button>
          {step > 1 && (
            <>
              <div>What kind of laptop?</div>
              <br/>
              <button onClick={handleNextStep}>Windows</button>
              <button onClick={handleNextStep}>IOS</button>
            </>
          )}

          {step > 2 && (<div>when did you buy it?</div>)}
        </div>
      )
    }

const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<App />)
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<body>
  <div id="#root"/>
</body>

Comments

0

Not 100% sure about your question, but it seems you have several steps, so you could switch activeStep and render nessesary content based on activeStep

class App extends React.Component {

  constructor() {
    super(); 
    this.state = { activeStep: 1 }
  }

  moveToNextStep = (step) => {
    this.setState({
      activeStep: step
    });
  }

  render() {
    return (
     {/* STEP 1 */}
     {this.state.activeStep === 1 && (
        <div>
          Do you have a laptop <br/>
          {/* move to step 2 */}
          <button onClick={() => this.moveToNextStep(2)}>yes</button>
          {/* move to some other step... */}
          <button onClick={() => {}}>no</button>
        </div>
      )}
      {/* STEP 2 */}
      {this.state.activeStep === 2 && (
         <div>
           What kind of laptop?<br/>
           {/* move to step 3 */}
           <button onClick={() => this.moveToNextStep(3)}>Windows</button>
           {/* move to some other step and etc.... */}
           <button onClick={() -> {}}>IOS</button>
         </div>
      )}
      {/* STEP 3 */}
      {this.state.activeStep === 3 && (
         <div>
           when did you buy it?
         </div>
      )}
  }
}

Comments

0

I am a bit confused about your second comment but for the first comment you can wrap your buttons and render them based on a condition.

import React from "react";
class App extends React.Component {
  constructor() {
    super();
    this.state = { showMessage: false, os: "" };
  }

  _showMessage = (bool) => {
    this.setState({
      showMessage: bool
    });
  };

  _setOs = (value) => {
    this.setState({
      os: value
    });
  };

  render() {
    return (
      <div>
        Do you have a laptop <br />
        <button onClick={this._showMessage.bind(null, true)}>yes</button>
        <button onClick={this._showMessage.bind(null, false)}>no</button>
        {this.state.showMessage && <div>What kind of laptop?</div>}
        <br />
                // when user clicks yes, buttons will appear

        {this.state.showMessage === true && (
          <div>
            {/* I want to hide these buttons until the 'yes' button is clicked*/}
            <button onClick={this._setOs.bind(null, "Windows")}>Windows</button>
            <button onClick={this._setOs.bind(null, "IOS")}>IOS</button>
            <button onClick={this._setOs.bind(null, "")}>None</button>
          </div>
        )}
        {/* I want to display a third button which says none of the above */}

             // If use clicks windows or IOS, it will show him the next 
             //question. otherwise it wont show anything
        {!!this.state.os && <div>when did you buy it?</div>}
      </div>
    );
  }
}

export default App;

please explain your logic clearly so I can understand it and edit my answer and complete rest of your question.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.