Not able to add active class in React.js - reactjs

I'm trying to add an active class only on click event but it's adding in all events I have three texts each should have active class when it's clicked please see what's wrong in my below code,
class CategoryList extends React.Component {
state = {
productlist: [],
isActive: false
};
comingSoon(e) {
this.setState(activate => {
return { productlist: data.comingsoon, isActive: !activate.isActive };
});
}
boxOffice(e) {
this.setState(activate => {
return { productlist: data.boxoffice, isActive: !activate.isActive };
});
}
newRelease(e) {
this.setState(activate => {
return { productlist: data.newrelease, isActive: !activate.isActive };
});
}
render() {
return (
<div className="categoryList-container">
<div className="categoryList-text-wrapper">
<h5
className={this.state.isActive ? "active" : ""}
onClick={() => this.comingSoon(this)}
>
COMING SOON
</h5>
<h5
className={this.state.isActive ? "active" : ""}
onClick={() => this.boxOffice(this)}
>
BOX OFFICE
</h5>
<h5
className={this.state.isActive ? "active" : ""}
onClick={() => this.newRelease(this)}
>
NEW RELEASE
</h5>
</div>

Use three different variables .
class CategoryList extends React.Component {
state = {
productlist: [],
isActiveComing: false,
isActiveBox: false,
isActiveNew: false
};
comingSoon(e) {
this.setState(activate => {
return { productlist: data.comingsoon, isActiveComing: !activate.isActiveComing };
});
}
boxOffice(e) {
this.setState(activate => {
return { productlist: data.boxoffice, isActiveBox: !activate.isActiveBox};
});
}
newRelease(e) {
this.setState(activate => {
return { productlist: data.newrelease, isActiveNew: !activate.isActiveNew};
});
}
render() {
return (
<div className="categoryList-container">
<div className="categoryList-text-wrapper">
<h5
className={this.state.isActiveComing? "active" : ""}
onClick={() => this.comingSoon(this)}
>
COMING SOON
</h5>
<h5
className={this.state.isActiveBox ? "active" : ""}
onClick={() => this.boxOffice(this)}
>
BOX OFFICE
</h5>
<h5
className={this.state.isActiveNew ? "active" : ""}
onClick={() => this.newRelease(this)}
>
NEW RELEASE
</h5>
</div>

It happens because your three methods comingSoon, boxOffice and newRelease are changing the same variable in your state. You should create one variable for each h5 so you can handle their activation isolated.
Another solution is to create another component that renders your h5 element and import it into this one.

class CategoryList extends React.Component {
state = {
productlist: [],
isActive: 0
};
comingSoon(e) {
this.setState(activate => {
return { productlist: data.comingsoon, isActive: 1};
});
}
boxOffice(e) {
this.setState(activate => {
return { productlist: data.boxoffice, isActive: 2};
});
}
newRelease(e) {
this.setState(activate => {
return { productlist: data.newrelease, isActive: 3};
});
}
render() {
return (
<div className="categoryList-container">
<div className="categoryList-text-wrapper">
<h5
className={this.state.isActive === 1 ? "active" : ""}
onClick={() => this.comingSoon(this)}
>
COMING SOON
</h5>
<h5
className={this.state.isActive === 2 ? "active" : ""}
onClick={() => this.boxOffice(this)}
>
BOX OFFICE
</h5>
<h5
className={this.state.isActive === 3 ? "active" : ""}
onClick={() => this.newRelease(this)}
>
NEW RELEASE
</h5>
</div>

You are using the same state item for all three of the links, so they all get switch on/off at the same time.
Either use a different state item for each, or make your state item hold a distinct value depending on which link is active.
Since not two links can be active at the same time i would go with the second option, of using different values.
Something like
class CategoryList extends React.Component {
state = {
productlist: [],
activeSection: ''
};
comingSoon(e) {
this.setState(activate => {
return { productlist: data.comingsoon, activeSection: 'coming-soon'};
});
}
boxOffice(e) {
this.setState(activate => {
return { productlist: data.boxoffice, activeSection: 'box-office' };
});
}
newRelease(e) {
this.setState(activate => {
return { productlist: data.newrelease, activeSection: 'new-release' };
});
}
render() {
const {activeSection} = this.state;
return (
<div className="categoryList-container">
<div className="categoryList-text-wrapper">
<h5
className={activeSection == 'coming-soon' ? "active" : ""}
onClick={this.comingSoon}
>
COMING SOON
</h5>
<h5
className={activeSection == 'box-office' ? "active" : ""}
onClick={this.boxOffice}
>
BOX OFFICE
</h5>
<h5
className={activeSection == 'new-release' ? "active" : ""}
onClick={this.newRelease}
>
NEW RELEASE
</h5>
</div>
</div>
);
}

Instead of using three different variable use one and provide them values according to there name that make sense.
class CategoryList extends React.Component {
state = {
productlist: [],
isActive: 'COMING_SOON'
};
comingSoon(e) {
this.setState(activate => {
return { productlist: data.comingsoon, isActive: 'COMING_SOON'};
});
}
boxOffice(e) {
this.setState(activate => {
return { productlist: data.boxoffice, isActive: 'BOX_OFFICE'};
});
}
newRelease(e) {
this.setState(activate => {
return { productlist: data.newrelease, isActive: 'NEW_RELEASE'};
});
}
render() {
return (
<div className="categoryList-container">
<div className="categoryList-text-wrapper">
<h5
className={this.state.isActive === 'COMING_SOON'? "active" : ""}
onClick={() => this.comingSoon(this)}
>
COMING SOON
</h5>
<h5
className={this.state.isActive === 'BOX_OFFICE' ? "active" : ""}
onClick={() => this.boxOffice(this)}
>
BOX OFFICE
</h5>
<h5
className={this.state.isActive === 'NEW_RELEASE' ? "active" : ""}
onClick={() => this.newRelease(this)}
>
NEW RELEASE
</h5>
</div>

Related

How to toggle class on onclick in map function?

Toggling on a single element is a breeze. But I'm having a difficulty toggling with map function. Clicking a child element should toggle "active". But what's happening is that every element gets the class "active". Take a look:
constructor() {
super()
active: false
this.toggleClick = this.toggleClick.bind(this)
}
toggleClick() {
this.setState(state=> ({
active: !state.active
})
)
}
...in my function class...
function ThisClass(props){
return(
<div>
{
items.map((item,i) => {
return(
<span role="button" className={`${props.active ? 'active' : ''}`} key={i} onClick={() => props.toggleClick(i)}>{item.text}</span>
)
})
}
</div>
)
}
This is my desired output:
<div>
<span class="active">A</span>
<span class="">B</span>
<span class="">C</span>
</div>
Instead, this becomes the result
<div>
<span class="active">A</span>
<span class="active">B</span>
<span class="active">C</span>
</div>
And of course, toggling should be working. a single click would make the current active. And clicking it again would remove the active state. Also, By clicking the current state, the previous active element should be stripped off with active.
Here's my own solution(along with #404notBrighton): In my state:
...instead of
this.state = {active:false}
I've changed it to
this.state = {active:null}
in my toggleClick() I've put
this.setState({ active: i });
if (this.state.active === i) {
this.setState({ active: null })
}
then finally in my class...
<span role="button" className={`${props.active === i ? 'active' : ''}`} key={i} onClick={() => props.toggleClick(i)}>
try assigning your "active" state an index instead of a boolean
change your code
from
className={${props.active ? 'active' : ''}}
to
className={${props.active === i ? 'active' : ''}}
Your toggle function must look like this
toggleClick(i) {
this.setState({ active: i })
}
As I understand, you want to have only one active item.
You need to keep two state like selectedItemId and selectedItemState.
And update their values when button is clicked.
So you can try something like this:
import React, { Component } from "react";
class App extends Component {
state = {
items: [{ id: 1, text: "A" }, { id: 2, text: "B" }, { id: 3, text: "C" }],
selectedItemId: null,
selectedItemState: false
};
toggleClick = id => {
if (id === this.state.selectedItemId) {
this.setState({
selectedItemState: !this.state.selectedItemState
});
} else {
this.setState({
selectedItemId: id,
selectedItemState: true
});
}
};
render() {
const { selectedItemId, selectedItemState } = this.state;
return this.state.items.map(el => (
<div key={el.id}>
<span
role="button"
className={
el.id === selectedItemId && selectedItemState ? "active" : ""
}
onClick={() => this.toggleClick(el.id)}
>
{el.text} -{" "}
{el.id === selectedItemId && selectedItemState ? "active" : "passive"}
</span>
<br />
</div>
));
}
}
export default App;
Sample codesandbox:
https://codesandbox.io/s/spring-thunder-w711z

Toggle class only on one element, react js

I`m changing class after clicking and it works.
The problem is that, classes change simultaneously in both elements and not in each one separately. Maybe someone could look what I'm doing wrong. Any help will be useful.
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
this.setState({
isExpanded: !this.state.isExpanded
});
}
render() {
const { isExpanded } = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>First</strong>
</p>
</div>
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>Second</strong>
</p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
Every element must have its seperate expanded value. So we need an array in state.
And here is the code:
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
state = {
items: [
{ id: 1, name: "First", expanded: false },
{ id: 2, name: "Second", expanded: true },
{ id: 3, name: "Third", expanded: false }
]
};
handleToggle = id => {
const updatedItems = this.state.items.map(item => {
if (item.id === id) {
return {
...item,
expanded: !item.expanded
};
} else {
return item;
}
});
this.setState({
items: updatedItems
});
};
render() {
return this.state.items.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={el.expanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
<span> {el.expanded.toString()}</span>
</p>
</div>
));
}
}
export default PageContentSupportFaq;
You can get two state one state for first and another for a second and handle using two function like this
import React, { Component } from 'react';
class PageContentSupportFaq extends Component {
constructor(props) {
super(props)
this.state = {
isExpanded: false,
isExpanded2:false,
}
}
handleToggle(e){
this.setState({
isExpanded: !this.state.isExpanded
})
}
handleToggle2(e){
this.setState({
isExpanded2: !this.state.isExpanded2
})
}
render() {
const {isExpanded,isExpanded2} = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div onClick={(e) => this.handleToggle(e)} className={isExpanded ? "active" : "dummy-class"}>
<p className="mb-0"><strong>First</strong></p>
</div>
<div onClick={(e) => this.handleToggle2(e)} className={isExpanded2 ? "active" : "dummy-class"}>
<p className="mb-0"><strong>Second</strong></p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
You'll need to track toggled classes in array, that way it will support arbitrary number of components:
// Save elements data into array for easier rendering
const elements = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
expanded: []
};
}
handleToggle(id) {
this.setState(state => {
if (state.isExpanded.includes(id)) {
return state.isExpanded.filter(elId => elId !== id);
}
return [...state.expanded, id];
});
}
render() {
return elements.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={this.isExpanded(el.id) ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
</p>
</div>
));
}
}

How to update the state into a function in react

I am creating an application with tabs. When clicking on any of the tabs you should update the class index-big by modifying the state of this.state.addActiveTabs but this does not happen.
I get the impression that this.state.addActiveTabs comes empty and that's why the class never changes, but I do not understand why.
Edit: I think the problem comes from that openTabs is a function with a push that creates the elements and can not update the content of a push of a function.
openTabs is called from the brother component of Tabs. It is called when clicking on a menu item. When it is called is when it creates a tab and at the same time a div/iframe
The problem is that the .push creates the html and after creating it within a function I am not able to access it. How could I do it? I need to change only one class, not to update the whole element or anything.
class App extends Component {
constructor(props, context){
super(props, context);
["openTabs", "removeTab", "activeTabs",].forEach((method) => {
this[method] = this[method].bind(this);
});
this.displayData = [];
this.state = {
navigation: {
menu: [],
},
tabs:{
tabsLi: [],
},
divIframe:{
tabsDivIframe: [],
},
tabsiframe: '',
showtabs: true,
showdata: this.displayData,
postVal: "",
addActiveTabs: "",
};
}
openTabs(e, url, iframe, trdtitle){
//Evitar apertura automatica href
e.preventDefault();
//Cambiar la primera letra a mayuscula y las demas a minusculas
function firstUppercase(string){
return string.charAt(0).toUpperCase() + string.slice(1);
}
trdtitle = firstUppercase(trdtitle.toLowerCase());
url = url.toLowerCase();
//Cambiar el estado
this.setState({
showtabs: false,
})
//Creacion de las tabs + mostrar componentes
if (this.state.tabs.tabsLi.includes(trdtitle) === false){
if(iframe === 'si'){
console.log(this.state.addActiveTabs);
this.displayData.push(<div key={trdtitle.replace(/ /g, "")} id={"myTab"+trdtitle.replace(/ /g, "")} className={this.state.addIndexTabs === trdtitle ? ' index-big' : ''}><iframe title={"iframe"+trdtitle} className="iframetab" src={url}></iframe></div>);
}
else{
this.displayData.push(<div key={trdtitle.replace(/ /g, "")} id={"myTab"+trdtitle.replace(/ /g, "")} className={this.state.addIndexTabs === trdtitle ? ' index-big' : ''}><div className="iframetab">{url}</div></div>);
}
this.setState({
tabs: { tabsLi:[...new Set(this.state.tabs.tabsLi),trdtitle].filter(function(el) { return el; })},
showdata : this.displayData,
postVal : trdtitle,
})
}
}
activeTabs(value){
this.setState({
addActiveTabs: value,
})
return () => this.setState({
addIndexTabs: value,
});
}
render(){
return (
<>
<Tabs
navigation={this.state.navigation}
textvalue={this.state.textvalue}
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
divIframe={this.state.divIframe}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
tabsiframe={this.state.tabsiframe}
showdata={this.state.showdata}
addActiveTabs={this.state.addActiveTabs}
openTabs={this.openTabs}
removeTab={this.removeTab}
displayData={this.displayData}
activeTabs={this.activeTabs}
/>
</>
)
}
}
class Tabs extends Component {
constructor(props, context){
super(props, context);
["showCloseTabs", "hideCloseTabs"].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
closeTabs: false,
};
}
showCloseTabs(index, value){
this.setState({
closeTabs : true,
valueTabs: value,
})
}
hideCloseTabs(){
this.setState({
closeTabs: false,
})
}
render(){
return(
<div id="content-tabs" className="tabs">
{( this.props.showtabs)
? (
<>
<div className="waiting-leads">
<p>Parece que todavía no hay ningún lead...</p>
<h3>¡Ánimo, ya llega!</h3>
<img src={imgDinosaurio} alt="Dinosaurio"></img>
</div>
</>
) : (
<>
<ul id="resizable" className="content" >
<LiTabs
tabsLi={this.props.tabs.tabsLi}
closeTabs={this.state.closeTabs}
addActiveTabs={this.props.addActiveTabs}
valueTabs={this.state.valueTabs}
removeTab={this.props.removeTab}
activeTabs={this.props.activeTabs}
showCloseTabs={this.showCloseTabs}
hideCloseTabs={this.hideCloseTabs}
/>
</ul>
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
tabsiframe={this.props.tabsiframe}
displayData={this.props.displayData}
/>
</>
)}
</div>
);
}
}
class LiTabs extends Component{
render(){
return(
<>
{this.props.tabsLi.map((value, index) =>
<li key={index}
onClick={(e) => this.props.activeTabs(value)}
onMouseEnter={(e) => this.props.showCloseTabs(e, value)}
onMouseLeave={(e) => this.props.hideCloseTabs(e, value)}
className={this.props.addActiveTabs === value ? ' active' : ''}>
<span>{value}</span>
<div onClick={this.props.removeTab.bind(this, value, index)} >
{this.props.closeTabs && this.props.valueTabs === value &&(
<Icon icon="cerrar" className='ico-cerrar'/>
)}
</div>
</li>
)}
</>
);
}
}
class DivAndIframe extends Component{
render(){
return(
<div className="content-modules">
{this.props.displayData}
</div>
);
}
}
Make a use of closure to hold the value of your tab value in the onClick handler like below:
activeTabs(value){
return () => this.setState({
addActiveTabs: value,
});
}
and then on your onClick, change it to below:
<li key={index}
onClick={(e) => this.props.activeTabs(value)}
className={this.props.addActiveTabs === value ? ' active' : ''}>
<span>{value}</span>
</li>

Cannot change the state of parent component and re-render

im new to React, trying to make some simple 'Chat' app, stuck a bit in some feature.
im trying to make user list, that onClick (on one of the user) it will change the class (to active), and when hitting another user it will set the active class to the new user.
tried a lot of things, managed to make it active, but when hitting another user, the old one & the one receive the 'active' class.
here is my Parent componenet
class Conversations extends React.Component {
constructor(props) {
super(props);
this.loadConversations = this.loadConversations.bind(this);
this.selectChat = this.selectChat.bind(this);
this.state = { count: 0, selected: false, users: [] }
}
selectChat = (token) => {
this.setState({ selected: token });
}
loadConversations = (e) => {
$.get('/inbox/get_conversations', (data) => {
let r = j_response(data);
if (r) {
this.setState({ count: r['count'], users: r['data']});
}
});
}
componentDidMount = () => {
this.loadConversations();
}
render() {
return (
<div>
{this.state.users.map((user) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} />)
})}
</div>
)
}
here is my Child componenet
class User extends React.Component {
constructor(props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
token: this.props.token,
selected: this.props.selected,
username: this.props.username
}
}
handleSelect = (e) => {
//this.setState({selected: e.target.dataset.token});
this.props.selectChat(e.target.dataset.token);
}
render() {
return (
<div data-selected={this.props.selected} className={'item p-2 d-flex open-chat ' + (this.props.selected == this.props.token ? 'active' : '')} data-token={this.props.token} onClick={(e) => this.handleSelect(e)}>
<div className="status">
<div className="online" data-toggle="tooltip" data-placement="right" title="Online"></div>
</div>
<div className="username ml-3">
{this.props.username}
</div>
<div className="menu ml-auto">
<i className="mdi mdi-dots-horizontal"></i>
</div>
</div>
)
}
Any help will be great...hope you can explain me why my method didnt work properly.
Thank you.
You can make use of index from map function to make element active.
Initially set selected to 0;
this.state = { count: 0, selected: 0, users: [] }
Then pass index to child component,also make sure you render your User component when you are ready with data by adding a condition.
{this.state.users.length > 0 && this.state.users.map((user,index) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} index={index} />)
})}
In child component,
<div data-selected={this.props.selected} className={`item p-2 d-flex open-chat ${(this.props.selected === this.props.index ? 'active' : '')}`} data-token={this.props.token} onClick={() => this.handleSelect(this.props.index)}>
...
</div>
handleSelect = (ind) =>{
this.props.selectChat(ind);
}
Simplified Demo using List.

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>
)
}

Resources