React. How to add class in element on click? - javascript

I'm new to React. Currently I'm creating one app in learning purposes.
I'm stuck trying to change color of button on click. When I click on a button, the color of all the buttons are changed. But I need to have just one button active, while others are not active. When I click on different button, the previous active button loses its class and become inactive.
Could you help me out?
state = {
clicked: false
};
timeChangeHandler = () => {
this.setState({ clicked: true });
};
render() {
return (
<div>
<button
type="button"
className={
this.state.clicked
? "list-group-item list-group-item-action active"
: "list-group-item list-group-item-action"
}
onClick={this.timeChangeHandler}
>
8:00
</button>
<button
type="button"
className={
this.state.clicked
? "list-group-item list-group-item-action active"
: "list-group-item list-group-item-action"
}
onClick={this.timeChangeHandler}
>
9:00
</button>
</div>
);
}

Here is a simple example of how you can do it. I map the buttons and use index as the clicked value. But, if you are using your buttons statically as in your code you can use #Kyle's solution.
const buttons = [
{
name: "foo"
},
{
name: "bar"
},
{
name: "baz"
}
];
class App extends React.Component {
state = {
clicked: null
};
handleClick = id => this.setState({ clicked: id });
render() {
return (
<div>
{buttons.map((button, index) => (
<button
className={this.state.clicked === index && "clicked"}
onClick={() => this.handleClick(index)}
>
{button.name}
</button>
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
.clicked {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

Your solution would look something like this.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeButton: 0
};
}
render() {
const { activeButton } = this.state;
return (
<div>
<button
className={ activeButton === 0 && "active" }
onClick={() => this.setState({activeButton: 0})}
/>
<button
className={ activeButton === 1 && "active" }
onClick={() => this.setState({activeButton: 1})}
/>
<button
className={ activeButton === 2 && "active" }
onClick={() => this.setState({activeButton: 2})}
/>
</div>
);
}
}

Related

React Toggle ID

I have an issue with a toggling div. It is probably a small thing, but I can not find the reason why it works the first time I click on it (and the panel expands), but when clicking again it does not close. Maybe someone can see why the second time I click on the item, the id is not being send along with the element "e"? Thanks a lot!
class FlexPanel extends Component {
constructor(props) {
super(props);
this.state = {
toggle1: false,
toggle2: false,
toggle3: false,
};
}
render() {
const toggleOpen = (e) => {
const id = e.target.id;
const toggleId = `toggle${id}`;
let toggleItem = this.state[toggleId];
this.setState({
[toggleId]: !toggleItem,
});
};
const { toggle1, toggle2, toggle3 } = this.state;
console.log(toggle1);
return (
<div className="panels">
<div
id={1}
className={`panel panel1 ${toggle1 ? "open open-active" : "closed"} `}
onClick={(e) => {
toggleOpen(e);
}}
>
<p>Consultes</p>
<p>Teràpies</p>
<p>Recolzament</p>
</div>
<div
id={2}
className={`panel panel2 ${toggle2 ? "open open-active" : "closed"} `}
onClick={(e) => {
toggleOpen(e);
}}
>
<p>Videoconsultes</p>
<p>en grup</p>
<p>i individuals</p>
</div>
<div
id={3}
className={`panel panel3 ${toggle3 ? "open open-active" : "closed"} `}
onClick={(e) => {
toggleOpen(e);
}}
>
<p>Jordi</p>
<p>Arjó</p>
<p>Teràpies</p>
</div>
</div>
);
}
}
export default FlexPanel;
In toggleOpen() you need to change const id = e.target.id; to const id = e.currentTarget.id
Check out the modified code here
More about difference between e.target and e.currentTarget in the official documentation
You need to use updater function for setState
this.setState((prevSate) => {
let toggleItem = prevState[toggleId];
return {
[toggleId]: !toggleItem,
}
});

Render multiple components without changing route path

My goal is to render a Component without changing its url pathname. This Component is responsible to render three other child components when user clicks on the link. My code is working, but I don't think it is the right approach. My question is: What would be the best way of implementing this. Here's my code below:
const ComponentOne = () => {
return (
<div><h1>Component 1</h1></div>
)
}
const ComponentTwo = () => {
return (
<div><h1>Component 2</h1></div>
)
}
const ComponentThree = () => {
return (
<div><h1>Component 3</h1></div>
)
}
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
linkName: 'three'
};
this.renderList = <ComponentThree/>;
}
handleClick = (linkName) => {
if (linkName === 'one') {
this.renderList = <ComponentOne/>;
this.setState({ linkName });
} else if (linkName === 'two') {
this.renderList = <ComponentTwo />;
this.setState({ linkName });
} else {
this.renderList = <ComponentThree />;
this.setState({ linkName });
}
return this.renderList;
};
render() {
return (
<div>
<ul>
<li><a
className={this.state.linkName === 'one' ? 'active' : ''}
onClick={() => this.handleClick('one')}>
Component 1
</a></li>
<li><a
className={this.state.linkName === 'two' ? 'active' : ''}
onClick={() => this.handleClick('two')}>
Component 2
</a></li>
<li><a
className={this.state.linkName === 'three' ? 'active' : ''}
onClick={() => this.handleClick('three')}>
Component 3
</a></li>
</ul>
{this.renderList}
</div>
)
}
}
ReactDOM.render(<Header/>, document.querySelector('#root'))
to better demonstrate here is a link to codepen:
codepen
Your code can be simplified by keeping the index in the state and then rendering based on the current index. Here's my slightly simplified solution:
https://codepen.io/olavih/pen/zYGobaV
return (
<div>
<h1>Component 1</h1>
</div>
);
};
const ComponentTwo = () => {
return (
<div>
<h1>Component 2</h1>
</div>
);
};
const ComponentThree = () => {
return (
<div>
<h1>Component 3</h1>
</div>
);
};
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
linkIndex: 3
};
}
handleClick = linkIndex => {
this.setState({ linkIndex });
};
render() {
return (
<div>
<ul>
<li>
<a
className={this.state.linkIndex === 1 ? "active" : ""}
onClick={() => this.handleClick(1)}
>
Component 1
</a>
</li>
<li>
<a
className={this.state.linkIndex === 2 ? "active" : ""}
onClick={() => this.handleClick(2)}
>
Component 2
</a>
</li>
<li>
<a
className={this.state.linkIndex === 3 ? "active" : ""}
onClick={() => this.handleClick(3)}
>
Component 3
</a>
</li>
</ul>
{this.state.linkIndex === 1 && <ComponentOne />}
{this.state.linkIndex === 2 && <ComponentTwo />}
{this.state.linkIndex === 3 && <ComponentThree />}
</div>
);
}
}
ReactDOM.render(<Header />, document.querySelector("#root"));
<a> tag by default will reload the page.
You can pass the event object from onClick action (onClick={(e) => this.handleClick(e,'three')}) to your handleClick method and in that method block reloading of the page with e.preventDefault().
You can also use React Router to switch from using default HTML <a> to <Link>. Link component is rendering <a> but it does not reload the entire page after clicking.

Showing 1 of 2 components on button click

On the parent component, I am trying to do 2 buttons, each of them will show a component, the first button shows itemlist and second component shows itemlist2 but I couldn't seem to get it right, I tried to follow this example (https://codepen.io/PiotrBerebecki/pen/yaVaLK) even though I'm not sure its the right resource for such feature, here is my app.js code
class App extends Component {
state = {
one: false
};
handleClick(e) {
const userChoice = e.target.className;
this.setState({
userChoice
});
}
toggleDiv() {
this.setState({
one: !this.state.one
});
}
toggleDiv1() {
this.setState({
one: this.state.one
});
}
render() {
return (
<div>
<NavBar />
<div className="container-fluid">
<ServiceSelector toggleDiv={this.toggleDiv.bind(this)} toggleDiv1=
{this.toggleDiv1.bind(this)} />
{this.state.one == false ? <ItemList /> : <ItemList2 />}
</div>
</div>
);
}
}
class ServiceSelector extends React.Component {
toggleDiv() {
this.props.toggleDiv();
}
toggleDiv2() {
this.props.toggleDiv2();
}
render() {
return (
<div>
{" "}
<button onClick={this.toggleDiv.bind(this)}>sss </button>
<button onClick={this.toggleDiv1.bind(this)}>sss </button>
</div>
);
}
}
Actually, this function
toggleDiv1() {
this.setState({
one: this.state.one
});
}
is useless.
The toggle function should have one
toggleFunc() {
this.setState({stateWatched = !this.state.stateWatched})
}
Use this function in both case(set true or false). And don't bind when call in ServiceSelector component, this is nonesense.
class App extends Component {
state = {
one: false
};
handleClick(e) {
const userChoice = e.target.className;
this.setState({
userChoice
});
}
toggleDiv() {
this.setState({
one: !this.state.one
});
}
render() {
return (
<div>
<NavBar />
<div className="container-fluid">
<ServiceSelector toggleDiv={this.toggleDiv.bind(this)} />
{this.state.one == false ? <ItemList /> : <ItemList2 />}
</div>
</div>
);
}
}
class ServiceSelector extends React.Component {
render() {
return (
<div>
{" "}
<button onClick={this.props.toggleDiv}>sss </button>
</div>
);
}
}
If you want to have 2 buttons to handle toggle. Change the logic of the function.
function toggleTrue() {
this.setState({one: true})
}
function toggleFalse() {
this.setState({one: false})
}
and then pass it like normal (remember to remove bind function in a child component )
I am not too sure about what you trying to do but i am gonna give it a try.
class App extends Component {
state = {
one: false
};
handleClick(e) {
const userChoice = e.target.className;
this.setState({
userChoice
});
}
toggleDiv = () => {
this.setState({
one: !this.state.one
});
}
toggleDiv1 = () => {
this.setState({
one: this.state.one
});
}
render() {
return (
<div>
<NavBar />
<div className="container-fluid">
<ServiceSelector toggleDiv={this.toggleDiv} toggleDiv1={this.toggleDiv1} />
{this.state.one == false ? <ItemList /> : <ItemList2 />}
</div>
</div>
);
}
}
class ServiceSelector extends React.Component {
render() {
return (
<div>
{" "}
<button onClick={this.props.toggleDiv}>sss </button>
<button onClick={this.props.toggleDiv1}>sss </button>
</div>
);
}
}
Using arrow functions remove the need of typing this.bind as it does the binding for you
Let me know if it helps

How to add a class to only the specifically clicked element in React?

Im looping over an object and displaying li tags and attaching a click handler.
Below that is a game component that displays further information.
Can someone help me with my logic and how to tell React to only add the class to the specific component related to the click.
at
Currently when I click one element, all of the following game components open at the same time. I just want the one below to open.
Library Component
class Library extends React.Component {
state = {
active: false
};
toggleClass = index => {
let active = this.state.active;
active = !active;
this.setState({ active });
};
render() {
return (
<section>
<h2>Game Library</h2>
<ul>
{this.props.games.map((game, index) => (
<Fragment key={`${index}-${game.name}`}>
<li
key={`${index}-${game.gameId}`}
onClick={() => this.toggleClass(index)}
>
{game.name}
</li>
<Game
key={index}
index={index}
game={this.props.games[index]}
active={this.state.active}
/>
</Fragment>
))}
</ul>
</section>
);
}
}
Game Component
class Game extends React.Component {
render() {
return (
<div
key={this.props.index}
className={this.props.active ? "show" : "hide"}
>
<p>{this.props.game.name}</p>
<p>{this.props.game.gameId}</p>
<a>Close</a>
</div>
);
}
}
Instead of having a boolean active that you use for each game, you could use an object with a key for each index that indicates if that particular game is active.
Example
class Library extends React.Component {
state = {
active: {}
};
toggleClass = index => {
this.setState(previousState => {
const active = { ...previousState.active };
active[index] = !active[index];
return { active };
});
};
render() {
return (
<section>
<h2>Game Library</h2>
<ul>
{this.props.games.map((game, index) => (
<Fragment key={`${index}-${game.name}`}>
<li
key={`${index}-${game.gameId}`}
onClick={() => this.toggleClass(index)}
>
{game.name}
</li>
<Game
key={index}
index={index}
game={game}
active={this.state.active[index]}
/>
</Fragment>
))}
</ul>
</section>
);
}
}

React js: Accessing state of other components

I have a component built using the below code. The aim is to add a class on the card to highlight it when the button inside it is clicked. However, the below code works on the first click but doesn't work for the subsequent clicks.
I understood that I have to set the clicked state of other elements to false when I remove the class. How can this be done?
import React, { Component } from 'react';
import './PricingCard.css';
class PricingCard extends Component {
constructor(){
super();
this.state = {
clicked : false
}
}
makeSelection(){
let elems = document.getElementsByClassName('Card');
for(var i=0;i<elems.length;i++){
elems[i].classList.remove("active");
}
this.setState({clicked: true});
}
render() {
var activeClass = this.state.clicked ? 'active' : '';
return (
<div className= {"categoryItem Card " + this.props.planName + " " +activeClass}>
<div className="cardDetails">
<div> {this.props.planName} </div>
<div className="pricing"> {this.props.price} </div>
<button onClick={this.makeSelection.bind(this)} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {this.props.footerText} </div>
</div>
</div>
);
}
}
export default PricingCard;
Wouldn't it be easier to have the logic in a parent component? Since it is "aware" of all the child Card components.
Have something like...
this.state = { selectedComponent: null };
onClick(card_id) {
this.setState({ selectedComponent: card_id });
}
...in render:
const cards = smth.map((card) =>
<Card onClick={this.onClick.bind(this, card.id)}
isActive={map.id === this.state.selectedComponent} />
Would this work?
Best way will be to lift lift the state up. Like this:
class PricingCardContainer extends React.Component {
constructor(props){
super(props);
this.state = {
selectedCard: NaN,
}
}
handleCardClick(selectedCard){ this.setState({ selectedCard }); }
render() {
return (
<div>{
this.props.dataArray.map((data, i) =>
<PricingCard
key={i}
className={this.state.selectedCard === i ? 'active': ''}
price={data.price}
onClick={() => this.handleCardClick(i)}
footerText={data.footerText}
planName={data.planName}
plan={data.plan}
/>
)
}</div>
)
}
}
const PricingCard = ({ className = '', planName, price, onClick, footerText }) => (
<div className= {`categoryItem Card ${planName} ${className}`}>
<div className="cardDetails">
<div> {planName} </div>
<div className="pricing"> {price} </div>
<button onClick={onClick} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {footerText} </div>
</div>
</div>
);
export default PricingCard;
Although it would be better to use some data id than index value.

Categories