How to implement a React container which can handle all events from its sub-components? - reactjs

Assume that the container contains 3 buttons:
<Container>
<Button1/> <Button2> <Button3/>
</Container>
By default, all buttons are in 'Normal' state. If I click on Button1, I'd like to have Button1 becomes 'Active' state (with solid background) and Button2 & Button3 become 'Inactive' state (with gray border).
What's the best way to achieve that in React?

Name is a valid html attribute to use in buttons. If you want to use React to control this, you can simply have a state property attributed to the selected button's name. With that state, you can control the disabled attribute, as well as the class name.
this.state = {
activeButton: null
};
onButtonClick = (e) => {
const { name } = e.target;
this.setState({
activeButton: name
});
};
render() {
const { activeButton } = this.state;
return (
<Component>
<button
className={`${activeButton === 'one' && 'active' || ''}`}
onClick={this.onButtonclick}
name="one"
disabled={activeButton !== 'one'}>
Button 1
</button>
<button
className={`${activeButton === 'two' && 'active' || ''}`}
onClick={this.onButtonclick}
name="two"
disabled={activeButton !== 'two'}>
Button 2
</button>
<button
className={`${activeButton === 'three' && 'active' || ''}`}
onClick={this.onButtonclick}
name="three"
disabled={activeButton !== 'one'}>
Button 2
</button>
</Component>
);
}

You can consider using button as a child component
class Button extends React.Component {
constructor(props) {
super(props);
this.setActive = this.setActive.bind(this);
}
setActive() {
this.props.onClick(this.props.value);
}
render() {
return (<button onClick={this.setActive} className={this.props.active ? 'active' : ''}>
{this.props.children}
</button>);
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 0,
};
this.buttons = Array.from({ length: 5 }).map((v, index) => index);
this.setActive = this.setActive.bind(this);
}
setActive(active) {
this.setState({ active })
}
render() {
return (<div>
{
this.buttons.map((value) => (<Button key={value} value={value} active={this.state.active === value} onClick={this.setActive}>Button #{value}</Button>))
}
</div>);
}
}

Working Demo
Make your component look like below, this will work for sure:
class App extends React.Component {
constructor(props) {
super(props)
this.setActiveBackground = this.setActiveBackground.bind(this);
this.state = {
selectedCircle: {
},
};
}
setActiveBackground(name, event) {
let selected = this.state.selectedCircle;
selected = {};
selected[name] = this.state.selectedCircle[name] == "selected" ? "" : "selected";
this.setState({selectedCircle: selected});
}
render() {
return (
<div className="container">
<div className="row">
<div className="card-panel white">
<div className="center">
<p>Set Active background in selected button</p>
<button onClick={this.setActiveBackground.bind(this, "first")} className={this.state.selectedCircle["first"]}>Button 1</button>
<button onClick={this.setActiveBackground.bind(this, "second")} className={this.state.selectedCircle["second"]}>Button 2</button>
<button onClick={this.setActiveBackground.bind(this, "third")} className={this.state.selectedCircle["third"]}>Button 3</button>
</div>
</div>
</div>
</div>
);
}
}
Check demo for output

Related

Handle multiple child component in React

I've tried to look everywhere and couldn't find anything related to my use case, probably I'm looking for the wrong terms.
I have a situation where I have a bar with 3 icons, I'm looking for set one icon "active" by changing the class of it.
The icon is a custom component which have the following code
export default class Icon extends Component {
state = {
selected : false,
}
setSelected = () => {
this.setState({
selected : true
})
}
setUnselected = () => {
this.setState({
selected : false
})
}
render() {
var classStatus = '';
if(this.state.selected)
classStatus = "selected-icon"
else
classStatus = "icon"
return <div className={classStatus} onClick={this.props.onClick}><FontAwesomeIcon icon={this.props.icon} /></div>
}
}
In my parent component I have the following code
export default class MainPage extends Component {
handleClick(element) {
console.log(element);
alert("Hello!");
}
render() {
return (
<div className="wrapper">
<div className="page-header">
<span className="menu-voice">File</span>
<span className="menu-voice">Modifica</span>
<span className="menu-voice">Selezione</span>
</div>
<div className="page-main">
<span className="icon-section">
<div className="top-icon">
<Icon icon={faFileCode} onClick={() => this.handleClick(this)} />
<Icon icon={faCodeBranch} onClick={() => this.handleClick(this)} />
<Icon icon={faMagnifyingGlass} onClick={() => this.handleClick(this)} />
</div>
</span>
<span className="files-section">Files</span>
<span className="editor-section"></span>
</div>
<div className="page-footer">
Footer
</div>
</div>
);
}
}
What I'm trying to achieve is that when one of the Icon child component get clicked it will set the selected state to true manage by the parent component, in the same time while one of them is true I would like that the parent would set to false the other twos.
I've tried to use the useRef function but it doesn't look as a best practise.
Which is the correct way to do it? Sending also this to the handleClick function it just return the MainPage class instead of the child. Any suggestion at least where I should watch?
Thanks in advance
I suggest not storing the state in the icon, since it doesn't know what else you're using it for. Simply have the icon component take it's 'selected' status from props. e.g.
export default class Icon extends Component {
render() {
var classStatus = '';
if(this.props.selected)
classStatus = "selected-icon"
else
classStatus = "icon"
return (
<div className={classStatus} onClick={this.props.onClick}>.
<FontAwesomeIcon icon={this.props.icon} />
</div>
);
}
};
Then you can just manage the state in the parent where it should be:
export default class MainPage extends Component {
constructor(props) {
super(props);
this.state = { selectedOption : '' };
}
handleSelectOption(newValue) {
this.setState({ selectedOption: newValue });
}
isSelected(value) {
return value === this.state.selectedOption;
}
render() {
return (
<div className="wrapper">
{ /* etc... */ }
<div className="page-main">
<span className="icon-section">
<div className="top-icon">
<Icon
icon={faFileCode}
onClick={() => this.handleSelectOption("File")}
selected={isSelected("File")}
/>
<Icon
icon={faCodeBranch}
onClick={() => this.handleSelectOption("Modifica")}
selected={isSelected("Modifica")}
/>
{ /* etc... */ }
</div>
</span>
</div>
{ /* etc... */ }
</div>
);
}
};
You should define a constructor in your class component:
constructor(props) {
super(props);
this.state = { selected : false };
}
You also have to call a function which modify the state when you click on the Icon. onClick={this.props.onClick} doesn't change the state

Add class to following buttons inside a map function

I have a function that adds class onClick.
import * as React from 'react'
class ThisClass extends React.Component<any,any> {
constructor(){
super()
this.state = {active: ''}
this.ThisFunction = this.ThisFunction.bind(this)
}
const items = ['button1', 'button2', 'button3']
ThisFunction (i) {
this.setState({
active: i
})
}
render(){
return(
<div>
{
items.map((item, i) => {
return(
<button
onClick={()=>this.ThisFunction(i)}>{item}
className={`this_button ${this.state.active ? 'active' : ''}`}
{item}
</button>
)
})
}
</div>
)
}
}
export default ThisClass
What's suppose to happen:
<div>
<button class='this_button active'>button1</button>
<button class='this_button active'>button2</button>
<button class='this_button'>button3</button>
</div>
What's really happening:
<div>
<button class='this_button active'>button1</button>
<button class='this_button'>button2</button>
<button class='this_button'>button3</button>
</div>
I need to have a maximum of two active' class onClick function. Is there any way to get around this?
Change your state to hold an array of active indices and change the handler to only maintain the last two set (i.e. kick out the least recently used)
state = {
active: []
};
thisFunction = i => {
if (this.state.active.includes(i)) return; // already active!!
const active = [...this.state.active, i].slice(-2); // keep last 2
this.setState({ active });
};
You also had some syntax errors in the button rendering. The classname was being set as part of the button text. Set the active class if this.state.active array includes the active index
{["button1", "button2", "button3"].map((item, i) => {
return (
<button
onClick={() => this.thisFunction(i)}
className={`this_button ${
this.state.active.includes(i) ? "active" : ""
}`}
>
{item}
</button>
);
})}
There are some major errors in your code.
You are putting the className property as a child on the button, is an attribute instead
Every child on a list e.g your buttons needs to have a key
You are binding twice the function, one on the constructor and another one on the onClick event using an arrow function. Bind a function
class ThisClass extends React.Component {
constructor(){
super()
this.state = {active: ''}
}
ThisFunction (i) {
this.setState({
active: i
})
}
render(){
const items = ['button1', 'button2', 'button3']
return(
<div>
{
items.map((item, i) => {
return(
<button
key={i}
onClick={() => this.ThisFunction(i)}
className={`this_button ${this.state.active === i ? 'active' : ''}`}>
{item}
</button>
)
})
}
</div>
)
}
}
ReactDOM.render(
<ThisClass />,
document.body
);
<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>

Add a class for the active button ReactJS

When you click on a button, you should change its class. When I click on one of the buttons, the class changes for both. I need the class to be added only to the active button, and if I click on another, then the first button will have the class disappear and the second will appear. Where is the error?
import React, { Component } from 'react';
class trueName extends Component {
constructor(props) {
this.state = {
name: {},
};
}
editName = (names)=>{
this.setState({name:names});
}
someFunct(name) {
this.setState({ active: name })
}
render() {
const {name}=this.state;
return(
<div >
<div className="SelectName">
<span>{this.state.name}</span>
</div>
<button
className={this.state.active === name ? 'active' : ''}
onClick={ () => {this.editName(John);this.someFunct(name)}}
key ='1'>
<span>My name John</span>
</button>
<button
className={this.state.active === name ? 'active' : ''}
onClick={ () => {this.editName(Donald);this.someFunct(name)}}
key ='2'
>
<span>My name Donald</span>
</button>
</div>
)
}
}
export default trueName;
You are setting state.name and then setting state.active to the same value, so this.state.active === this.state.name is always true and the active class gets applied.
This might help:
constructor(props) {
super(props)
this.state = {
name: null
}
}
editName = name => {
this.setState({ name: name })
}
render() {
const { name } = this.state
return (
<div>
<div className="SelectName">
<span>
<pre>{name}</pre>
</span>
</div>
<button
className={name === "John" ? "active" : ""}
onClick={() => {
this.editName("John")
}}
>
<span>My name John</span>
</button>
<button
className={name === "Donald" ? "active" : ""}
onClick={() => {
this.editName("Donald")
}}
>
<span>My name Donald</span>
</button>
</div>
)
}

React Toggle Active Class between multiple Reactstrap Buttons

I am new to React and Reactstrap. I am trying to set the active class or active attribute to a single button onclick. My current setup toggles all the buttons at the same time. What the best way to toggle the active states independently between the buttons states when clicked?
import React, { Component } from 'react'
import { Button } from "reactstrap";
export class Payment extends Component {
constructor(props) {
super(props);
this.state = {
paymentSelection: 'credit',
active: false
}
}
toggleContent = (event) => {
const currentState = this.state.active;
event.preventDefault();
this.setState({
paymentSelection: event.target.value,
active: !currentState
})
}
switchContent = (value) => {
switch (value) {
case 'credit':
return <div>
<p>Credit Content Here</p>
</div>;
case 'paypal':
return <div>
<p>PayPal Content Here</p>
</div>;
case 'amazon':
return <div>
<p>Amazon Content Here</p>
</div>;
case 'more':
return <div>
<p>More Content Here</p>
</div>;
default:
return null;
}
}
render() {
const { paymentSelection } = this.state;
const { active } = this.state;
return (
<div className="container-fluid payment-btn-group">
<Button outline className={active ? 'active': null} color="secondary" value="credit" onClick={this.toggleContent} >Credit Card</Button>
<Button outline className={active ? 'active': null} color="secondary" value="paypal" onClick={this.toggleContent} >PayPal</Button>
<Button outline className={active ? 'active': null} color="secondary" value="amazon" onClick={this.toggleContent} >Amazon</Button>
<Button outline className={active ? 'active': null} color="secondary" value="more" onClick={this.toggleContent} >+ More</Button>
{this.switchContent(paymentSelection)}
</div>
);
}
}
export default Payment
You can set active class like this
className={paymentSelection ==='credit' ? 'active' : null}
You already have payment selection in the state you just need to check that apply it.
Demo

className gets removed clicking outside the div

I have the following code. By default Patriots will have btn-primary color which is blue and packers will have the default color of white. The color changes upon selection between the two buttons but when I click outside the div both button change to white. Really appreciate any suggestion. I am an absolute beginner btw.
class something {
constructor(props) {
super(props);
this.state = {
clicked: false
};
}
toggleView() {
this.setState({
clicked: true
});
}
render() {
var classRender = "btn-primary";
classRender = this.state.clicked ? "btn-primary" : "btn-primary selected ";
return (
<div>
<button
className={classRender}
type="button"
onClick={e => this.toggleView()}
>
Patriots
</button>
<button
className="btn-primary"
type="button"
onClick={e => this.toggleView()}
>
Packers{" "}
</button>
</div>
);
}
}
I just tested the code below it will update the button with selected class onClick and will keep selected if clicked outside
export default class Toggle extends Component {
constructor(props) {
super(props);
this.state = {
patriotsSelected : true
}
this.patriotsSelected = this.patriotsSelected.bind(this);
this.packersSelected = this.packersSelected.bind(this);
}
patriotsSelected(){
this.setState({patriotsSelected : true})
}
packersSelected(){
this.setState({patriotsSelected : false})
}
render() {
var patriotsClass = this.state.patriotsSelected ? "btn-primary selected" : "btn-primary";
var packersClass = this.state.patriotsSelected ? "btn-primary" : "btn-primary selected";
return (
<div>
<button className={patriotsClass} onClick={this.patriotsSelected} >
Patriots
</button>
<button className={packersClass} onClick={this.packersSelected} >
packers
</button>
</div>
);
}
}
So what's happening is even if you click the Packers button, the Patriots button still thinks it's clicked, because your assignment of 'this.state.clicked' is ambiguous in the toggleView method.
class something {
constructor(props) {
super(props);
this.state = {
clicked: null
};
}
toggleView(team) {
this.setState({
clicked: team
});
}
buttonClass(team) {
var clicked = this.state.clicked;
return (team === clicked ? "btn-primary" : "btn-primary selected ");
}
render() {
return (
<div>
<button
className={this.buttonClass('Patriots')}
type="button"
onClick={e => this.toggleView('Patriots')}
>
Patriots
</button>
<button
className={this.buttonClass('Packers')}
type="button"
onClick={e => this.toggleView('Packers')}
>
Packers{" "}
</button>
</div>
);
}
}

Resources