BTM
BTM

Reputation: 3

Is there a cleaner way to write this React code?

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

Answers (4)

akhtarvahid
akhtarvahid

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

hotpink
hotpink

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

Babak Yaghoobi
Babak Yaghoobi

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

naortor
naortor

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

Related Questions