0

There is a function called Question that takes a list of objects (questionList) and changes each object into a React component that shows a question and when clicked should reveal the answer in a FAQ style. There is another function called Answer which shows the answer when a Question container is clicked. However, instead of showing the answer of just the container which was clicked, it shows every answer of all the questions - as if rendered all the questions again with their answers. When a container is clicked again, all Answer containers are hidden.

How do I only show the answer of the specific Question container?

Example:

  1. How does this work?
  2. What do you do?

click on 2

  1. How does this work?
    • Answer to 1
  2. What do you do?
    • Answer to 2

Instead of:

  1. How does this work?
  2. What do you do?
    • Answer to 2
function Question(props) {
    const [show, setShow] = useState(false);
    const onClick = () => {
        if (show) {
            setShow(false);
        } else {
            setShow(true);
        }
    };

    const questionList = props.questionList;
    const questions = questionList.map(question => (
        <div onClick={onClick} className="container">
            <div className="line"></div>
            <div className="question-container">
                <h3 className="question">{question.question}</h3>
                <div className="drop-icon">
                    <i className="fa-solid fa-caret-down"></i>
                </div>
            </div>

            {show ? <Answer question={question} /> : null}
        </div>
    ));
    return <section>{questions}</section>;
}

function Answer(props) {
    const question = props.question;
    const answer = (
        <div className="answer">
            <p>{question.answer}</p>
        </div>
    );
    return answer;
}

1 Answer 1

1

It seems that the state show is just one value while each item should probably have their own show for the desired result.

Try make show an object (or array) that stores a boolean value for each item in the list:

function Question({ questionList }) {
  const [show, setShow] = useState({});
  const onClick = (index) =>
    setShow((prev) => ({ ...prev, [index]: !Boolean(prev[index]) }));

  const questions = questionList.map((question, index) => (
    <div key={index} onClick={() => onClick(index)} className="container">
      <div className="line"></div>
      <div className="question-container">
        <h3 className="question">{question.question}</h3>
        <div className="drop-icon">
          <i className="fa-solid fa-caret-down"></i>
        </div>
      </div>

      {show[index] ? <Answer question={question} /> : null}
    </div>
  ));
  return <section>{questions}</section>;
}

Or consider to make each question a component QuestionItem (like Answer in the example) that has its own show state:

const QuestionItem = ({ question }) => {
  const [show, setShow] = useState(false);
  const onClick = () => setShow((prev) => !prev);
  return (
    <div onClick={onClick} className="container">
      <div className="line"></div>
      <div className="question-container">
        <h3 className="question">{question.question}</h3>
        <div className="drop-icon">
          <i className="fa-solid fa-caret-down"></i>
        </div>
      </div>
      {show ? <Answer question={question} /> : null}
    </div>
  );
};

And iterate the component QuestionItem in Question:

function Question({ questionList }) {
  const questions = questionList.map((question, index) => (
    <QuestionItem key={index} question={question} />
  ));
  return <section>{questions}</section>;
}
Sign up to request clarification or add additional context in comments.

6 Comments

Could you explain how this line works: setShow((prev) => ({ ...prev, [index]: !Boolean(prev[index]) }));? I don't understand the ...prev, [index] part especially.
@tices Take a look at developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… and open a new question if you have a specific thing in mind.
@tices This line pass a setter function for setShow, which gets prev with the previous value as an argument. The return {...prev, [index]: !Boolean(prev[index])} updates the object with existing values, only changing the related key from true to false, or from undefined or false to true. Check updating-objects-in-state from React docs for a complete guide of this topic.
It does make a lot more sense now though thanks to the documentation.
@tices The return is a new object (to avoid mutating the state), and {...prev} makes a copy of prev before updating the key [index], so in result you get a new object like the previous one, just had this key updated.
|

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.