Solution 1 :

you can have a separate state for the selected accordion like below

const [selected, setSelected] = useState("");

.... 

const toggle = (type) => {
    type === selected ? setShowServices(false) : setShowServices(true);
    setSelected(type);
  };

and check the condition while rendering as

<div id="srvs" key={t.id}>
      {showServices && selected === type ? t.info : ""}  // like so
</div>

Please check the below sandbox and fork it for your reference

  • I’m not sure of styling to answer your other Q sorry

Edit cool-estrela-hf7plv

Solution 2 :

As I guss from part of your code, you binded single state to all Service related info.

The state showServices is shared between all item and when it changed from one item, then affect all other items.

Define new child component for info like example that i prepare for you.

function ServiceDetail({ service }) {
    const [show, setShow] = React.useState(false);
    function toggle() {
        setShow(s => !s);
    }

 return   <div className="detail">
        <h4 className="title">{service.name}</h4>
        <span style={{ cursor: 'pointer', fontSize: '20px', fontWeight: 'bold' }} id='showBtn'
            onClick={toggle}>+</span>
        <div className="accordion">
            {service.detail.map((t, index) => (
                <div  key={index} className="content" id='services'>
                    <div id='srvs'>
                        {show ? t : ''}
                    </div>
                </div>
            ))}
        </div>
    </div>
}

function Service() {
    const [services, setServices] = React.useState(
        [
            {
                id: 1,
                name: "Service 1",
                detail: ["list-1","list-2","list-3"]
            },
            {
                id: 2,
                name: "Service 2",
                detail: ["bb"]
            },
            {
                id: 3,
                name: "Service 3",
                detail: ["bb"]
            },
            {
                id: 4,
                name: "Service 4",
                detail: ["bb"]
            },
            {
                id: 5,
                name: "Service 5",
                detail: ["bb"]
            }
        ]
    );
    return <div className="service">
        {
            services.map((service) => {
                return <ServiceDetail key={service.id} service={service} />
            })
        }
    </div>
}



function App() {
    return <Service />;
}

ReactDOM.render(< App className="app" />, document.getElementById("root"));
.app{
width:100%;
}
.service{
display:grid;
column-gap: 1rem;
align-items:flex-start;
align-content:flex-start;
row-gap: 2rem;
grid-template-columns: 1fr 1fr 1fr;
}
.detail{
color:#9c772d;
background:#eeeeee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Edit: For size problem, just you need to add following css to container as i add to sample:

align-items:flex-start;
align-content:flex-start;

Problem :

I’m facing some issues with my React application.

First I will explain what I want in my app then I will talk about the issues.

I have a list of 6 Services and I want to wrap each service list in Accordion. Whenever the user clicks the “+” button for the specific service I want that list to be displayed with slides down with a growing margin-bottom, depending on the number of items in the respective list without overlapping the below element.

While trying to achieve this, I’m facing the following issues.

1- when I click “+” button it toggles all 6 services and their list items are getting displayed.

2- all list items overlapping the below elements.
enter image description here

I tried with useState()

const [showServices, setShowServices] = useState(false);
  const toggle = () =>{    
   setShowServices((prevState) => !prevState);
}

<div className="services__service__description">
  <h4 className="title">Services</h4>
  <span style={{cursor: 'pointer', fontSize: '20px', fontWeight: 'bold'}} id='showBtn' 
    onClick={toggle}>+</span>
  <div className="accordion">
    {text.map((t) => (                    
      <div className="content" id='services'>
         <div id='srvs' key={t.id}>
            {showServices ? t.info : ''}
         </div>
      </div>                                        
    ))}
   </div>
</div>

codesandbox link

Comments

Comment posted by KcH

please add a reproducible sample … try codesandbox / stackblitz for making one, then write, save code and share link

Comment posted by SMH

thanks for your reply @KcH. I have added codesandbox link, kindly check the same.

Comment posted by Aliasghar Ahmadpour

Again I checked your codesandbox. you are using single state for all Item review my answer to understand what happend

Comment posted by SMH

@AliasgharAhmadpour, Yes I got it completely. I have to use another state, I check yours and KcH answers both are not addressing to my second question.

Comment posted by SMH

thank you @KcH, it resolves the first issue. But, it still overlaps the below elements. How can I wrap it in accordion and when open, push the below elements down. I want the height of each service to be dynamic which depends on the number of list items.

Comment posted by KcH

yeah, that’s what I’m poor at …. you can probably open a new Q tagging

Comment posted by SMH

thank you for your reply @Aliasghar. It will also overlap the below elements. How can I wrap it in accordion and when open, push the below elements down. I want the height of each service to be dynamic which depends on the number of list items.

Comment posted by SMH

Thank you @Aliasghar, I tried with the above 2 lines of CSS. It gives me all services in one line but my app layout is 3 & 3 as shown in the attached image.

By