Mi Azevedo
Mi Azevedo

Reputation: 39

Show/Hide elements of a list on click, one list item a time (react app)

I'm trying to make a list where, when you click on the title, it shows/hide the description below each one. It is partly working (when you click on a title, it shows all the descriptions and when you click again, it hides them all), but I need it to show/hide each item individualy.

import React from 'react'
import styled from 'styled-components'
import girl from '../assets/steps-image.png'
import arrow from '../assets/down-arrow.svg'

class StepsSection extends React.Component{
  constructor() {
    super();
    this.state = {
      show: true
    }
  }
render() {
  return (
    <StepsStyles  className="sections">
    <Illustration />
     <Content>
       <div><span className="tag">3 easy steps</span></div>
      <h1>How it works</h1>

Here we have the list:

      <ul>
        {Steps.map((lista, i)=>(
        <li key={i}>
        <div key={i} onClick={()=>{this.setState({show:!this.state.show})}} className="dropdown"><h2>{lista.title}</h2><img src={arrow} alt="" /></div>
        { this.state.show? <p>{lista.desc}</p> : null}
    </li>
        ))}
        
      </ul>
     </Content>
  </StepsStyles>
  )
}
}

export default StepsSection

This is just styled components:

const StepsStyles = styled.div`
  background: url(${girl}) no-repeat left;
  background-position: left center;
  background-size: contain;  
  margin-top: 200px;
  text-align: left;
  display: grid;
  height: 50vh;
  grid-template-columns:1fr 1fr;
  /* max-height: 50vh; */
  h1{
    font-weight: bold;
    margin-bottom: 5vh;
  }
`

  const Illustration = styled.div`
  grid: 1;
  `

  const Content = styled.div`
  grid: 2;
  text-align: left;
  img {
    max-width: 10px;
    max-height: 10px;
  }
  h2{
    font-weight: bold;
    font-size: 16px;
  }
  ul{
    width: 60%;
  }
  li {
    margin-bottom: 2vh;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--superlight-gray);
    ::last-child{
      border-bottom: none;
    }
    .dropdown {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 10px;
    }
  }
  ` 

And the json static data lays here on the bottom:

const Steps = [
    {
      title: '1. Download Tattoo master app (dropdown)',
      desc: 'Download the application in App Store and you acan find the wizard in any location around the world. '
    },
    {
      title: '2. Enter your location',
      desc: 'Download the application in App Store and you acan find the wizard in any location around the world.'
    },
    {
      title: '3. Find your tattoo master',
      desc: 'Download the application in App Store and you acan find the wizard in any location around the world.'
    }
  ]

here is the page: https://mastertattoo-landpage.netlify.app and repo:https://github.com/micazev/mastertattoo-landpage

Any ideas? Thanks in advance!

Upvotes: 1

Views: 1720

Answers (3)

Nelio
Nelio

Reputation: 766

You need to create a state for each one of your list elements. Then you can change them individually.

First I'd add an id to each one of the Steps item.

const Steps = [
    {
      id: 1,
      title: '1. Download Tattoo master app (dropdown)',
      desc: 'Download the application in App Store and you acan find the wizard in any location around the world. '
    },
    {
      id: 2,
      title: '2. Enter your location',
      desc: 'Download the application in App Store and you acan find the wizard in any location around the world.'
    },
    {
      id: 3,
      title: '3. Find your tattoo master',
      desc: 'Download the application in App Store and you acan find the wizard in any location around the world.'
    }
  ]

Second I'd change my show state to an Object. That way I can store the id of each item and them tell if it is true or false.

this.state = {
  show: {}
}

And finally I'd change the function to change each item state based on its id.

toggleListItem(itemId) {
  this.setState((prevState) => ({
    ...prevState,
    show: {
      ...prevState.show,
      [itemId]: !prevState.show[itemId]
    }
  }))
}

<div key={lista.id} onClick={() => this.toggleListItem(lista.id)} className="dropdown"> ... </div>

You can see it working here: https://codesandbox.io/s/priceless-browser-uxdr5

You can have a quick explanation about using prevState here: What is prevState in ReactJS?

Upvotes: 0

locemarn
locemarn

Reputation: 26

You can use something like this...

{Steps.map((lista, index) => (
  <details className='collapse'>
    <summary className='title'>{lista.title}</summary>
    <div className='description'>
      {lista.desc}
    </div>
  </details>
))}

details and summary are HTML tags for collapse.

Upvotes: 1

mohammad nowresideh
mohammad nowresideh

Reputation: 158

if you are trying to change the state by the value in the state please pass a function to setState as first argument like this:

this.setState((state, props) => ({
  show: !state.show
}));

Upvotes: 0

Related Questions