3

I tried to toggle individual item but unfortunately whenever I try to toggle an item the other item gets affected. Here is my code:

const FAQ = () => {
  const [open, setOpen] = useState(false);
  const [data, setData] = useState(faqData);
  return (
    <FAQSection>
      <FAQTitle>Frequently Asked Questions</FAQTitle>

      <Questions>
        <QuestionItemDetail>
          {data.map((item) => {
            const { id, question, answer } = item;

            return (
              <div key={id}>
                <QuestionItem onClick={() => setOpen(!open)}>
                  <QuestionItemTitle>{question}</QuestionItemTitle>
                  {open ? <Close /> : <Add />}
                </QuestionItem>
                <ReadQuestion>
                  {open && (
                    <ReadQuestionDetail>
                      <ReadQuestionDesc>{answer}</ReadQuestionDesc>
                    </ReadQuestionDetail>
                  )}
                </ReadQuestion>
              </div>
            );
          })}
        </QuestionItemDetail>
      </Questions>
    </FAQSection>
  );
};

What might be wrong with this because I ensured the dummy data has a unique ID.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Oge Obubu
  • 49
  • 10
  • What do you mean by "gets affected", can you put more details? – Vivek Bani Jun 26 '21 at 03:13
  • Does this answer your question? [Cannot set useState hook value to opposite boolean](https://stackoverflow.com/questions/57231752/cannot-set-usestate-hook-value-to-opposite-boolean) – Matt U Jun 26 '21 at 03:28

2 Answers2

1

Because you use a boolean to control all open/close. You need to use index/id to control this.

const [open, setOpen] = useState(null);
...
onClick={() => setOpen(preOpen => preOpen === id ? null : id)}
...
{open === id && (<ReadQuestionDetail>...</ReadQuestionDetail>)}
Viet
  • 12,133
  • 2
  • 15
  • 21
1

Your open state is used for all of the items in your data array, which is why it affects all of the items when toggled.

I recommend:

  1. putting all of the data item html/jsx inside a new component.
  2. Inside this new component, create an open state like so:
const MyItemComponent = (id, question, answer) => {
  const [open, setOpen] = useState(false);
  return (
    <div key={id}>
      <QuestionItem onClick={() => setOpen(!open)}>
        <QuestionItemTitle>{question}</QuestionItemTitle>
        {open ? <Close /> : <Add />}
      </QuestionItem>
      <ReadQuestion>
        {open && (
          <ReadQuestionDetail>
            <ReadQuestionDesc>{answer}</ReadQuestionDesc>
          </ReadQuestionDetail>
        )}
      </ReadQuestion>
    </div>
  );
}

const FAQ = () => {
  const [data, setData] = useState(faqData);
  return (
    <FAQSection>
      <FAQTitle>Frequently Asked Questions</FAQTitle>

      <Questions>
        <QuestionItemDetail>
          {data.map((item) => {
            const { id, question, answer } = item;

            return (
              <MyItemComponent id={id} question={question} answer={answer} />
            );
          })}
        </QuestionItemDetail>
      </Questions>
    </FAQSection>
  );
};

This will give you an individual open state for each item.

charlieb
  • 306
  • 3
  • 4