Reputation: 2532
Currently upgrading from a jquery / php webapp to react. All is going well and I understand the concepts behind react etc. The only issue I can't find a workaround is how to dynamically add / delete a class based on where the user clicks. I need this because I have a few dropdowns which trigger when the user click it and need to be hidden if they click somewhere else.
From: Give a class of "selected" when clicked on another <a> then remove the class if it's the same <a>
I've taken this example as it is simple, Jquery solution to the problem:
var h2 = $("h2 a");
h2.on("click", function() {
if ($(this).is(".selected")) {
$(this).removeClass("selected");
} else {
h2.removeClass("selected")
.filter(this).addClass("selected")
}
});
How to mimic the same functionality in react (and / or redux)?
Upvotes: 2
Views: 12851
Reputation: 7308
The thing is, you want to toggle some data on click event and change the classname of a html element accordingly.
Your data that 'selected' class based on could come from anywhere, from parent or component state. You would do something like this :
<div className={ myData ? 'selected' : '' } ></div>
But there is a better way to display classname changes with a library called classnames. The same thing is accomplished as :
<div className={ classNames({ 'selected' : myData }) } ></div>
Until now, we have seen how to display changes on render function. Second thing you need is to listen to click events and fire the function that will eventually toggle the data that controls the 'selected' classname, in my example 'myData'.
Here is a working example, there might be various ways to accomplish this. But I strongly recommend using classnames library to toggle classnames.
The workaround about removing class when another element ( apart from li elements we observe ) clicked could be solved by a click event listener.
For instance :
import React, { Component } from 'react'
import classNames from 'classnames'
class DropDown extends Component {
constructor(props){
super(props)
this.state = {
activeSelected : ''
}
this.handleClick = this.handleClick.bind(this)
}
componentDidMount(){
global.document.addEventListener( 'click', this.handleClick, false )
}
componentWillUnmount(){
global.document.removeEventListener( 'click', this.handleClick, false )
}
handleClick(event){
if( event.target.className.includes('not-changing-css-class') &&
this.state.activeSelected !== ''
) this.setState( { activeSelected : '' } )
}
render(){
let { activeSelected } = this.state
return (
<ul>
<li
className={ classNames({
'not-changing-css-class' : true,
'selected' : activeSelected === 'item1'
}) }
onClick={ event => this.setState({ activeSelected : activeSelected === 'item1' ? '' : 'item1' }) }
>
Item 1
</li>
<li
className={ classNames({
'not-changing-css-class' : true,
'selected' : activeSelected === 'item2'
}) }
onClick={ event => this.setState({ activeSelected : activeSelected === 'item2' ? '' : 'item2' }) }
>
Item 2
</li>
<li
className={ classNames({
'not-changing-css-class' : true,
'selected' : activeSelected === 'item3'
}) }
onClick={ event => this.setState({ activeSelected : activeSelected === 'item3' ? '' : 'item3' }) }
>
Item 3
</li>
</ul>
)
}
}
Upvotes: 3
Reputation: 1919
You can hold the selected element index (or ID if you use IDs) in the component state. You can use redux store if you think this state will be relevant to any other component in your app, but starting with state is simpler.
Once you have this state. You can check within your render()
function whether a link is selected or not, by comparing with the component state. You would also update the selectedIndex whent the links are clicked.
A simple example to render the links could be as follows. Note that you can extract parts of these to be functions instead of using expressions in JSX.
```
allLinks.map( (link, index) =>
<a href={link.target} className={this.state.selectedIndex === index ? 'selected' : null}/>
)
```
Upvotes: 0