Reputation: 3
First time using react. Writing a piece of code where I want the element I click on to be highlighted, while the unselected elements get unhighlighted. Basically, I want the state to change to true based on the element I click, and the other states to be toggled to false.
My question is, is there a more elegant to write this code? I originally tried to base it on event.target
but I don't know how to bind a state to a specific element. While below does work for my purposes, I do wonder if there's a way to more dynamically use setState without having to write separate methods for each case...
export default class CharacterNav extends Component {
constructor(props) {
super(props);
this.state = {
profileHighlighted: true,
recruitHighlighted: false,
artsHighlighted: false,
talentsHighlighted: false,
affectionHighlighted: false,
}
this.changeProfileHighlight = this.changeProfileHighlight.bind(this);
this.changeRecruitHighlight = this.changeRecruitHighlight.bind(this);
this.changeArtsHighlight = this.changeArtsHighlight.bind(this);
this.changeTalentsHighlight = this.changeTalentsHighlight.bind(this);
this.changeAffectionHighlight = this.changeAffectionHighlight.bind(this);
this.resetNavState = this.resetNavState.bind(this);
}
changeProfileHighlight () {
this.setState({
profileHighlighted: true,
recruitHighlighted: false,
artsHighlighted: false,
talentsHighlighted: false,
affectionHighlighted: false
});
}
changeRecruitHighlight () {
this.setState({
profileHighlighted: false,
recruitHighlighted: true,
artsHighlighted: false,
talentsHighlighted: false,
affectionHighlighted: false
});
}
changeArtsHighlight () {
this.setState({
profileHighlighted: false,
recruitHighlighted: false,
artsHighlighted: true,
talentsHighlighted: false,
affectionHighlighted: false
});
}
changeTalentsHighlight () {
this.setState({
profileHighlighted: false,
recruitHighlighted: false,
artsHighlighted: false,
talentsHighlighted: true,
affectionHighlighted: false
});
}
changeAffectionHighlight () {
this.setState({
profileHighlighted: false,
recruitHighlighted: false,
artsHighlighted: false,
talentsHighlighted: false,
affectionHighlighted: true
});
}
render () {
let divClass = this.props.characterSelected ?
"blueBackground" :"hideElement";
let profilehighlightclass = this.state.profileHighlighted ? 'highlighted' : 'unhighlighted';
let recruithighlightclass = this.state.recruitHighlighted ? 'highlighted' : 'unhighlighted';
let artshighlightclass = this.state.artsHighlighted ? 'highlighted' : 'unhighlighted';
let talentshighlightclass = this.state.talentsHighlighted ? 'highlighted' : 'unhighlighted';
let affectionhighlightclass = this.state.affectionHighlighted ? 'highlighted' : 'unhighlighted';
return(
<div>
<ul className={divClass}>
<li><h3 onClick={this.changeProfileHighlight} name='profileHighlighted' className={profilehighlightclass}>Profile</h3></li>
<li><h3 onClick={this.changeRecruitHighlight} name='recruitHighlighted' className={recruithighlightclass}>Recruitment</h3></li>
<li><h3 onClick={this.changeArtsHighlight} name='artsHighlighted' className={artshighlightclass}>Arts</h3></li>
<li><h3 onClick={this.changeTalentsHighlight} name='talentsHighlighted' className={talentshighlightclass}>Talents</h3></li>
<li><h3 onClick={this.changeAffectionHighlight} name='affectionHighlighted' className={affectionhighlightclass}>Affection</h3></li>
</ul>
</div>
)
}
}
Upvotes: 0
Views: 97
Reputation: 9779
Optimized way
class App extends React.Component {
state = {
arr: [
{ id: 1, name: "profileHighlighted", title: "Profile" },
{ id: 2, name: "recruitHighlighted", title: "Recruitment" },
{ id: 3, name: "changeArtsHighlight", title: "Arts" },
{ id: 4, name: "talentsHighlighted", title: "Talents" },
{ id: 5, name: "affectionHighlighted", title: "Affection" }
],
heightlighted:''
};
changeProfileHighlight = id => {
this.setState({heightlighted:id})
};
render() {
return (
<div>
<ul>
{this.state.arr.map(({ name, id, title }) => (
<li>
<h3
onClick={() => this.changeProfileHighlight(id)}
name={name}
className={"profilehighlightclass"}
>
<span style={{color:this.state.heightlighted===id?'red':''}}>{title}</span>
</h3>
</li>
))}
</ul>
</div>
);
}
}
Upvotes: 1
Reputation: 3226
toggleHighlight = e => {
const copy = { ...this.state };
Object.keys(copy).forEach(key => (copy[key] = false));
this.setState({ ...copy, [e.target.id]: true }, () => console.log(this.state));
};
This will toggle the boolean value of your state properties.
getClassName = element =>
this.state[element] ? 'highlighted' : 'unhighlighted';
This will toggle your className
.
To improve accessibility, you should add a keyboard event. Albeit it's generally not recommended to have onClick
events on non-interactive elements like h3
.
<h3
onClick={this.toggleHighlight}
onKeyPress={this.toggleHighlight}
role="button"
id="profileHighlighted"
className={this.getClassName('profileHighlighted')}
>
Profile
</h3>
And do use map
do render your list as recommended in previous answeres
Upvotes: 0
Reputation: 1985
Just add and remove class name :
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const highlightMe=(e)=>{
var els = document.getElementsByClassName("highlight");
for(var i = 0; i < els.length; i++){
els.item(i).classList.remove("highlight");
}
e.target.classList.add("highlight");
}
return (
<div className="App">
<div>
<ul className={''}>
<li><h3 onClick={highlightMe} name='profileHighlighted' className={"profileHighlighted"}>Profile</h3></li>
<li><h3 onClick={highlightMe} name='recruitHighlighted' className={"recruitHighlighted"}>Recruitment</h3></li>
<li><h3 onClick={highlightMe} name='artsHighlighted' className={"artsHighlighted"}>Arts</h3></li>
<li><h3 onClick={highlightMe} name='talentsHighlighted' className={"talentsHighlighted"}>Talents</h3></li>
<li><h3 onClick={highlightMe} name='affectionHighlighted' className={"affectionHighlighted"}>Affection</h3></li>
</ul>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
output : https://codesandbox.io/s/tender-http-pl4xy
Upvotes: 0
Reputation: 2087
Yes, you can hold in your state the name of the highlighted element.
something like :
export default class CharacterNav extends Component {
constructor(props) {
super(props);
this.state = {
highlighted: "profile",
};
}
setHighlighted = e => {
this.setState({
highlighted: e.target.name,
});
};
toggleHighlight = selectedTab => {
return this.state.highlighted === selectedTab
? "highlighted"
: "unhighlighted";
};
render() {
let divClass = this.props.characterSelected
? "blueBackground"
: "hideElement";
return (
<ul className={divClass}>
<li>
<h3
onClick={this.setHighlighted}
name="profile"
className={this.toggleHighlight("profile")}
>
Profile
</h3>
</li>
<li>
<h3
onClick={this.setHighlighted}
name="recruit"
className={this.toggleHighlight("recruit")}
>
Recruitment
</h3>
</li>
<li>
<h3
onClick={this.setHighlighted}
name="arts"
className={this.toggleHighlight("arts")}
>
Arts
</h3>
</li>
<li>
<h3
onClick={this.setHighlighted}
name="talents"
className={this.toggleHighlight("talents")}
>
Talents
</h3>
</li>
<li>
<h3
onClick={this.setHighlighted}
name="affection"
className={this.toggleHighlight("affection")}
>
Affection
</h3>
</li>
</ul>
);
}
}
You can also improve your li rendering by iterating over an array
export default class CharacterNav extends Component {
constructor(props) {
super(props);
this.state = {
highlighted: 'profile'
}
this.setHighlighted = this.setHighlighted.bind(this);
}
setHighlighted(e) {
this.setState({
highlighted: e.target.name
});
}
render () {
let divClass = this.props.characterSelected ?
"blueBackground" :"hideElement";
return(
<div>
<ul className={divClass}>
['Profile', 'Recruitment', 'Talents', 'Affection'].map(item => {
const name = item.toLowerCase();
return (
<li>
<h3
onClick={this.setHighlighted}
name={name}
className={name === this.state.highlighted ? 'highlighted' : 'unhighlighted'}>
{item}
</h3>
</li>
)
})
</ul>
</div>
)
}
}
Upvotes: 1