Home > database >  How to show/hide one element from data array rendered dynamically with react
How to show/hide one element from data array rendered dynamically with react

Time:11-17

Still fairly new on understanding how to use React hooks. I have a list of questions that was dynamically rendered from an array of objects. When I click on a question, I want only that question's answer to show. Right now, when I click on a question, all of the answers show at the same time.

This is the file containing the data:

export const personalFaq = [
    {
        question: 'Question 1',
        answer: 'Answer 1 '
    },
    {
        question: 'Question 2',
        answer: 'Answer 2'
    },
    {
        question: 'Question 3',
        answer: 'Answer 3'
    },
    {
        question: 'Question 4',
        answer: 'Answer 4'
    },
    {
        question: 'Question 5',
        answer: 'Question 5'
    }

]

This is my component for rendering my list of questions:

import React, { useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import { personalFaq } from '../../questionData/personalFaq';

const Faq = () => {

    const [showAnswer, setShowAnswer] = useState(false);
    const onClick = () => setShowAnswer(!showAnswer);
        
    const renderQuestion = (question, index) => {
        
        return (
            <div key={index}>
                <p><span onClick={onClick}>
                    {!showAnswer ? (<i className="fas fa-angle-down m-1"></i>) : (<i className="fas fa-angle-up"></i>)}
                    </span>{question.question}</p>
                {showAnswer && (<p>{question.answer}</p>) }
            </div>
        )

    }

    return(
        <Container>
            <Container>
                <Container>
                    <h1>FAQ</h1>
                    <Row>
                        {personalFaq.map(renderQuestion)}
                    </Row>
                </Container>
            </Container>
        </Container>
    )
}

export default Faq;

Image of what is currently rendered: Dynamically Rendered List of Questions

CodePudding user response:

It's because you set 1 boolean value for tracking the visibility for all of the items, so if it's turned to true all of them will be displayed.

you also don't pass any parameters to the onClick function that can track which one of the items needs to be displayed or hide

CodePudding user response:

Your state is declared and used within the Faq component, thus it affects all renders within that component that uses it.

In this case the simplest solution is to create a separate component for RenderQuestion that manages its own state and thus should only expand within itself.

Something like this the below example should solve your issue. RenderQuestion is now its own component that manages its own state. It will be rendered for each item in the personalFaq array, and each item will have its own instance of the component with its own state management.

import React, { useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import { personalFaq } from '../../questionData/personalFaq';

const RenderQuestion = (question) => {
    const [showAnswer, setShowAnswer] = useState(false);
    const onClick = () => setShowAnswer(!showAnswer);
    return (
        <div>
            <p><span onClick={onClick}>
                {!showAnswer ? (<i className="fas fa-angle-down m-1"></i>) : (<i className="fas fa-angle-up"></i>)}
            </span>{question.question}</p>
            {showAnswer && (<p>{question.answer}</p>)}
        </div>
    )
}

const Faq = () => {
    return (
        <Container>
            <Container>
                <Container>
                    <h1>FAQ</h1>
                    <Row>
                        {personalFaq.map((question, index) => <RenderQuestion key={index} question={question} />)}
                    </Row>
                </Container>
            </Container>
        </Container>
    )
}

export default Faq;

CodePudding user response:

If what you want to do is to be able to see an answer at the time, then you should probably add an id to every item and then use that to set an activeQuestionId and only display that (I left you an example). If you want to be able to see answers independently of each. other, then you should create a local state in a separate component for each row.

import React, { useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import { personalFaq } from '../../questionData/personalFaq';

const Faq = () => {
  const [activeQuestionId, setActiveQuestionId] = useState(null)

  return (
    <Container>
      <Container>
        <Container>
          <h1>FAQ</h1>
          <Row>
            {personalFaq.map(({ id, question, answer }) => (
              <div key={id}>
                <p>
                  <span onClick={() => setActiveQuestionId(id)}>
                    <i className={`fas ${activeQuestionId === id ? 'fa-angle-up' : 'fa-angle-down m-1'}`}></i>
                  </span>
                  {question}
                </p>
                {activeQuestionId === id && <p>{answer}</p>}
              </div>
            ))}
          </Row>
        </Container>
      </Container>
    </Container>
  )
}

export default Faq
  • Related