I want to set the active class dynamically in react.js but it's not working!
I'm using the setState() method to change the selected item.
this line of code not work .
className={selectedCategoryId === item.id ? 'active' : ''}
I think the setState() function does not work correctly...
const {open, selectedProduct, productCategory, filteredProducts, selectedCategoryId} = this.state;
const categoryItems = productCategory.map((item) =>
<a key={item.id}
onClick={() => this.handleFilter(item.id)}
className={selectedCategoryId === item.id ? 'active' : ''}
// className={()=>this.isActive(item.id)}
className="pointer"
>{item.value}</a>
);
this does not change the class:
handleFilter = (id) => {
const filteredItem = this.state.productList.filter(x => x.categoryId == id);
this.setState({filteredProducts: filteredItem, selectedCategoryId: id});
}
but this change the className correctly when select I all tags:
handleRemoveFilter = () => {
this.setState({filteredProducts: this.state.productList, selectedCategoryId: 0});
}
//-------------------------------
<div className="tag-list">
<a onClick={this.handleRemoveFilter}
className="pointer"
className={ this.state.selectedCategoryId === 0 ? 'active' : ''}
>All tags</a>
{categoryItems}
</div>
If setState() works well, try this :
<a onClick={this.handleRemoveFilter}
className={ this.state.selectedCategoryId === 0 ? 'pointer active' : 'pointer'}
>All tags</a>
One of the most common ways is to use classnames which you can conditionally joining classNames together
var classNames = require('classnames');
class Button extends React.Component {
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
}
store classname in state along with selected item. You can just update the classname in state whenever required.
for eg,
<a key={item.id}
onClick={() => this.handleFilter(item.id)}
className={this.state.activeClassName}
where active classname can be updated inside handlefilter
We can toggle class name dynamically like below,
const [islight, setIslight] = useState(false)
const toggle = () => {
setIslight(!islight)
}
return (
<div className={`block ${islight ? "blocklight" : "blockdark"}`}>
<h2>Hello World</h2>
</div>
)
Related
I want to make a button check which add a new calssName to my list. I use a function to update a state and take the string. If you want to help me be more specific because I am a beginner. Thanks !
const [check, setCheck] = useState({
id: '',
status: false
});
This is the function. With 'on' I take the string to add to id.
let complet = (on) =>{
if(check.status == false){
setCheck({
id: on,
status: true
})
}
else {
setCheck({
id: on,
status: false
})
}
}
And how I Display the list and how I check correspond to the string.
return(
<div className='display'>
{ list.map( (list,index) => (
<div className={ check.status && check.id == list ? 'lista complet' : 'lista'} key= {index} id='lista' >
{list}
<button className='btnCheck' onClick={complet.bind(this, list)}> <FcCheckmark/> </button>
<button className='btnRemove' onClick={remove.bind(null, list)}> <BsTrash/> </button>
</div>
))}
</div>
)
If you want to store the checked ids and the unchecked ids, you must change your state variable because currently it can only stores a single element. However, it seems you are rendering a list of elements that can be checked individually
Here is a possible solution :
function App({list}) {
const [checkIds, setCheckIds] = useState(() => {
const item = localStorage.getItem('checkIds');
return item ? JSON.parse(item) : {};
});
// reset the checkIds when the list is updated
useEffect(() => setCheckIds({}), [list]);
// save the checkIds into the local storage
useEffect(() => {
localStorage.setItem('checkIds', JSON.stringify(checkIds));
}, [checkIds]);
function checkId(id) {
setCheckIds({...checkIds, [id]: true);
}
function uncheckId(id) {
setCheckIds({...checkIds, [id]: false);
}
return (
<div className='display'>
{list.map(id => (
<div key={id} id={id} className={check[id] ? 'lista complet' : 'lista'}>
{id}
<button className='btnCheck' onClick={() => checkId(id)}>
<FcCheckmark/>
</button>
<button className='btnRemove' onClick={() => uncheckId(id)}>
<BsTrash/>
</button>
</div>
))}
</div>
)
}
Hello I have a React app that has a menu.
The state has 2 arrays one with the names of the menu items as string which i managed to render with the map function and I would like to enumerate for each menu item on the onClick attribute a function from the array of functions that I have in my state.
This is the code:
class MeniuPF extends Component {
constructor(props) {
super(props);
this.state = {
collapseID: '',
lista:[
'Adeverinta venit','Bilant anual','Bilant semestrial',
],
listafunctii:[
this.toggleAdeverintaVenit,
this.toggleBilantAnual,
this.toggleBilantSemestrial,
],
listaMesaje:false,
bilantAnual:false,
bilantSemestrial:false,
};
this.toggleListaMesaje = this.toggleListaMesaje.bind(this);
this.toggleBilantAnual = this.toggleBilantAnual.bind(this);
this.toggleBilantSemestrial = this.toggleBilantSemestrial.bind(this);
}
toggleCollapse = collapseID => () => {
this.setState(prevState => ({
collapseID: prevState.collapseID !== collapseID ? collapseID : ''
}));
};
toggleBilantAnual() {
this.setState({bilantAnual:!this.state.bilantAnual})
this.setState({bilantSemestrial:false})
this.setState({adeverintaVenit:false})
this.setState(prevState => ({
collapseID: prevState.collapseID !== this.state.collapseID ? this.state.collapseID : ''
}));
}
toggleBilantSemestrial() {
this.setState({bilantAnual:false})
this.setState({bilantSemestrial:!this.state.bilantSemestrial})
this.setState({adeverintaVenit:false})
this.setState(prevState => ({
collapseID: prevState.collapseID !== this.state.collapseID ? this.state.collapseID : ''
}));
}
toggleAdeverintaVenit() {
this.setState({bilantAnual:false})
this.setState({bilantSemestrial:false})
this.setState({adeverintaVenit:!this.state.adeverintaVenit})
this.setState(prevState => ({
collapseID: prevState.collapseID !== this.state.collapseID ? this.state.collapseID : ''
}));
}
check() {
this.state.listafunctii.map((list) =>
console.log(this.state.listafunctii[0])
)
};
render() {
return (
<div>
<Router>
<MDBContainer>
<MDBNavbar
color='light-blue lighten-4'
style={{ marginTop: '20px' }}
light
>
<MDBContainer>
<MDBNavbarBrand>Declaratii persoane juridice</MDBNavbarBrand>
<MDBNavbarToggler
onClick={this.toggleCollapse('navbarCollapse1')}
/>
<MDBCollapse
id='navbarCollapse1'
isOpen={this.state.collapseID}
navbar
>
<MDBNavbarNav left>
<MDBNavItem >
{this.state.lista.map((list,i) =>
<MDBNavLink to='#!' onClick={this.state.listafunctii[i}>{list}</MDBNavLink>
)}
</MDBNavItem>
</MDBNavbarNav>
</MDBCollapse>
</MDBContainer>
</MDBNavbar>
</MDBContainer>
</Router>
<br></br>
{this.state.listaMesaje ? <ListaMesaje/>:null}
{this.state.bilantAnual ? <BilantAnual/>:null}
{this.state.bilantSemestrial ? <BilantSemestrial/>:null}
</div>
);
}
}
export default MeniuPF;
I have at onClick attribute this.state.listafunctii[0] because I thought it would enumerate the functions that I have written in the state array but I have checked what is returning with the check method and in the console it shows me the whole function. When I click a menu item it gives me this error:
TypeError: Cannot read property 'setState' of undefined
So it calls the function but it gives me this error...and when I console.log the map of the array it return me what is contained in the function. I would like to enumerate only the function call eg: this.toggleAdeverintaVenit only this to show in the onClick attribute.
Thanks in advance
I found the answer...I moved the binding of the methods before the state and it works...and adding in the onClick attribute
onClick={this.state.listafunctii[i]()}
Just like the screenshot above, I am using Semantic-UI where the selected menu gets highlighted.
As my code below, I've set state to whichever menu the user clicks on. My code below works fine, but I think this is inefficient ways to write code since I am basically calling two functions everytime I render just to change the switch out the class names.
Would there be any better way to achieve this?
Please advise.
const Navigation = () => {
const [selected, setSelected] = useState("Comments");
const onSelection = (e) => {
setSelected(e.target.textContent);
};
const commentActive = () => {
return selected === "Comments" ? "active" : "";
};
const searchActive = () => {
return selected === "Search" ? "active" : "";
};
return (
<div className="ui secondary pointing menu">
<a className={`item ${commentActive()}`} onClick={(e) => onSelection(e)}>
Comments
</a>
<a className={`item ${searchActive()}`} onClick={(e) => onSelection(e)}>
Search
</a>
</div>
);
};
I think you shouldn't hardcode the boolean conditions selected === 'Comments' as its bug prone, because if you decide to change the anchor's context without changing the condition you may have a bug: target.textContent !== 'Comments';
Instead use enums:
const NAV_SECTIONS = {
COMMENTS: "comments",
SEARCH: "search",
};
const Navigation = () => {
const [selected, setSelected] = useState(NAV_SECTIONS.COMMENTS);
return (
<div className="ui secondary pointing menu">
<a
className={`item ${selected === NAV_SECTIONS.COMMENTS ? "active" : ""}`}
onClick={() => setSelect(NAV_SECTIONS.COMMENTS)}
>
{/* We changed the content and the code didn't break */}
My Cool Comments
</a>
<a
className={`item ${selected === NAV_SECTIONS.SEARCH ? "active" : ""}`}
onClick={() => setSelect(NAV_SECTIONS.SEARCH)}
>
My Best Search
</a>
</div>
);
};
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,
}
});
i have this breadcrump component that map over props and renders a list of chip components like this:
class BreadCrumb extends React.Component {
render () {
const {
steps,
activeIndex
} = this.props;
const chips = steps
.map((step,index) => {
return <Chip
key={index}
title={step.category}
onClick = {()=> this.props.selectChip(index)} // this should be passed only if
// active == true
active={activeIndex >= index} />
})
return (
<div className="chip-container">
{chips}
</div>
)
}
}
i need to click on chips only if his active prop is true,
this is the chip component
class Chip extends React.Component {
render(){
const {
active,
title
} = this.props;
const activeClassName = active ? 'chip active' : 'chip';
return (
<div
className = {activeClassName}
onClick = {() => this.props.onClick()} >
<span>{title}</span>
</div>
)
}
}
how can i make chip clickable only if the active prop is true?
For further information selectChip() function sets the state of a component App, parent of Breadcrump component, so it is binded to App component.
You could e.g. make that onClick function as a class method and use a simple condition inside:
class Chip extends React.Component {
handleClick = () => {
if (this.props.active) {
this.props.onClick(); // call only if active props is true
}
}
render() {
const { active, title } = this.props;
const activeClassName = active ? 'chip active' : 'chip';
return (
<div
className = {activeClassName}
onClick = {this.handleClick}
>
<span>{title}</span>
</div>
)
}
}
Either execute the handler or an empty function
onClick = {isActive ? this.props.onClick : () =>{} } >
You can do it like this:-
// If chip component expects a function all the time
<Chip
key={index}
title={step.category}
onClick = {step.active ? ()=> this.props.selectChip(index) : () => {}}
active={activeIndex >= index} />
// If onClick is an optional prop to chip component
<Chip
key={index}
title={step.category}
onClick = {step.active ? ()=> this.props.selectChip(index) : undefined}
active={activeIndex >= index} />
// of onClick handler is optional, possibly an alternative solution
type ChipProps = {
title: string;
active: boolean;
onClick?: ()=>void;
}
<Chip
key={index}
title={step.category}
active={activeIndex >= index}
{...(step.active ? {onClick:()=> this.props.selectChip(index)} : {})}
/>