Home > database >  React: how to toggle css class in a component based on 3 different state elements
React: how to toggle css class in a component based on 3 different state elements

Time:12-04

I have a Card component that has 4 potential states: "active", "selected", "discarded", "complete". Depending on the state, I need to display a different css class.

The states are set by clicking on specific parts of the card and each part is a "toggle" (one click sets it, another click "unsets" it):

  • default state is active,
  • if the user click on the card, it gets selected (the css class I need to add is "overlay-selected")
  • if the user click on "discard" btn inside the card it sets to "discarded" (the css class I need to add is "overlay-discarded" )
  • if the user click on "complete" btn inside the card it sets to "complete" (the css class I need to add is "overlay-complete" )

The state is stored at the "App" level and I am passing it to the component through props. This means that for updating the states I am passing some "handler" from the app to the component (SelectCard, DiscardCard)

Here below is my code.

I think they way I am approaching the problem is not ideal because I am having problem on how to best defyining the ClassName of the variable.

I initially thought about using a ternary if statement, but with more than one status it doesn't work. Especially because I need to take care of the "toggle" use-case.

App


function App() {
  const [people, setPeople] = useState(data['people'].map(
    el=>{
      return {
        ...el,
        'key' : nanoid(),
      }
    }
  ))

 

  function SelectCard(CardKey){
    setPeople(oldPeople=>{
      return oldPeople.map(el=>{

        return el.key === CardKey
                ? { ...el, 'selected':!el.selected}
                : { ...el, 'selected':false}
          })
    })
  }

  function DiscardCard(CardKey){
    setPeople(oldPeople=>{
      return oldPeople.map(el=>{

        return el.key === CardKey
                ? { ...el, 'active':!el.active}
                : { ...el}
          })
    })
  }

  const cards = people.map(el=>{
    return <Card 
            key = {el.key} 
            item={el} 
            onPress={()=>SelectCard(el.key)}
            onDiscard={()=>DiscardCard(el.key)}
          />
  })

  
  return (
    <div className="App">
      <div className='container'>
        <div className='left'>
          <div className='cards'>
            {cards}
          </div>
        </div>
        <div className = 'right'>
          ....
        </div>
     </div>
     
    </div>
  )
}

export default App


Card

function Card(props) {


  const className = `card ${props.item.selected ? "overlay-selected" : ""}`

  return (
    <div 
        className={className} 
        onClick={(event)=>{props.onPress()}}>

        <img className='card-img' alt='Card Image' src={props.item.img} />
        <h3 className='card-title'>{props.item.name} </h3>
        
        { props.item.selected ? 
        
            <div className='card-cta'>
                <button 
                    className='btn btn-back'
                    onClick={ props.item.selected ?  (event)=>
                        { 
                            event.preventDefault()
                            props.onPress
                        }
                         : ()=>{}}
                >Back</button>
                <button 
                    className='btn btn-discard'
                    onClick={ props.item.selected ?  (event) =>{
                            event.preventDefault()
                            props.onDiscard 
                        } 
                        :
                        ()=>{}}
                >Discard</button>
            </div>
        : 
        <p className='card-description'>{props.item.description} </p>  
    
        }
        
    </div>
  )
}


CodePudding user response:

Achieving this can be creating a function that maps the state of your card to the appropriate class name. This function can be called from within the Card component and would return the class name as a string.

function getClassName(state) {
  switch (state) {
    case "active":
      return "";
    case "selected":
      return "overlay-selected";
    case "discarded":
      return "overlay-discarded";
    case "complete":
      return "overlay-complete";
    default:
      return "";
  }
}

Then, in your Card component, you can call this function to get the appropriate class name based on the state of the card.

function Card(props) {
  const className = `card ${getClassName(props.item.state)}`;

  return (
    // ...
  )
}

So you can easily update the class name of your card based on its state by simply calling getClassName and passing in the current state of the card.

  • Related