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.