Reactjs hooks set active buttons - reactjs

I have this buttons from my backend
const ShiftCalendar = btn => {
console.log('btn ->', btn);
};
btnList.map((btn,i) => {
const is_active = btn.is_default ? 'is_active' : '';
return (
<Button
key={i}
variant="dark mt-3 mr-3 "
className={is_active}
size="sm"
onClick={() => ShiftCalendar(btn)}
>
{btn.title}
</Button>
);
})
How can I change active button by onclick event?
As you can See my button 1 has an is_active class, What I want is to change active by onclick of button. if i click button 2 button 1 will no longer have a is_active class while button 2 will have is_active class
Check button image here
BTW im using hooks not component.
THanks

Think of it this way. When we click on a button, we want to remember what button was clicked then when we rerender the page, lets mark the last clicked button as active and other not active.
So to do that in React, what you'd do is:
Firstly get the isDefault button from the array of buttons (either coming from an API, props or some constants) and store it as the default value in your state for the first render. If you don't have this key isDefault you can gracefully skip this step by making none of the buttons active or make the first active, whichever best suits your use case.
onClick of a button, call a function that you should take the clicked buttonId and save it in state.
Then on next render to determine which button is active by comparing the buttonId with what you stored in the state. If it matches then the className should be active, else any other classname you want.
import React, { useState } from 'react';
const btnList = [
{
id: 1,
title: 'Button 1',
isDefault: true,
},
{
id: 2,
title: 'Button 2',
isDefault: false,
}
];
const ButtonList = props => {
// 1. Get the default so we could set it in the state
const [defaultBtn] = btnList.some(btn => btn.isDefault === true);
const [activeButtonId, setActiveButtonId] = useState(defaultBtn ? defaultBtn.id : null);
// 2. Create an event handler so when we click on a button we save the buttonId in state
const handleButtonClick = event => {
setActiveButtonId(Number(event.target.value));
};
return (
<div>
{btnList.map(btn => (
<Button
key={btn.id}
variant="dark mt-3 mr-3 "
className={btn.id === activeButtonId ? 'is_active' : ''}// Compare the button's id to what we have in state to determine which should be active
size="sm"
value={btn.id} // Set the value of the button as the button's id
onClick={handleButtonClick}
>
{btn.title}
</Button>
))}
</div>
)
}
export default ButtonList;

Related

React => Counter Compontent in Slider => Counter in state increases before it decreases

I think I am making a rookie mistake in react here, but I was not able to resolve it on my own.
My Component is a "Selector", that renders a group of button-children as a button group. Using CSS I turn it into a "Snap-Slider. For navigation on mobile, i want to add navigation-buttons to that button-group (chevrons left and right).
I grab the buttons when component is mounted. Then, on click on either of the navigationbuttons of my slider-element, should slide the next one into view. This logic works, but is not refined. I plan on cleaning that up.
Now my problem: An example:
I load my page, the logger write active slide = 0 to the console
I click on next
Logger => Active slide = 0
Next button is not slided
I click next again
Logger => Active slide = 1
Next button IS slided
I click on prev
Logger => Active slide = 2
NEXT Button is slided again, so i am at button three now
When i no click on prev, the logger says active slide 1 and the previous button is slided into view
=> So TL;DR => When i increment/decrement my counter by clicking on either navigational button, the opposite direction is applied first, before the intended direction is applied.
What am i doing wrong here?
import React from 'react';
import SelectorButton from './SelectorButton';
class CategorySelector extends React.Component {
constructor(props){
super(props);
this.nextClick = this.nextClick.bind(this)
this.prevClick = this.prevClick.bind(this)
this.setSlide = this.setSlide.bind(this)
this.logger = this.logger.bind(this)
this.state = {
activeSlide: 0,
itemArray: []
}
}
logger() {
console.log(`actSlide => ${this.state.activeSlide}`)
}
componentDidMount() {
this.setState({itemArray: document.getElementsByClassName("selectorButton")})
this.logger()
}
nextClick() {
this.setState({activeSlide: this.state.activeSlide + 1})
let itemArr = Array.from(this.state.itemArray)
this.setSlide(itemArr[this.state.activeSlide])
this.logger()
}
prevClick() {
this.setState({activeSlide: this.state.activeSlide - 1})
let itemArr = Array.from(this.state.itemArray)
this.setSlide(itemArr[this.state.activeSlide])
this.logger()
}
setSlide(nextSlide) {
nextSlide.scrollIntoView({
behavior: 'smooth',
inline: 'start',
block: 'nearest'
})
}
render() {
return (
<>
<div className='selectorButton-scrollWrapper'>
<button id="prevbutton" className="selectorButton-navButton prev" onClick={this.prevClick}><span className="col-white"><i className="fa-solid fa-chevron-left"></i></span></button>
<div className="selectorButton-scrollBox py-2">
{
Array.from(this.props.categories).map(category => {
return(
<SelectorButton
category={category}
onShowDis={this.props.onShowDis}
key={`cat-${category.id}`}
allSelected={this.props.allSelected}
deselectAll={this.props.deselectAll}
/>
)
})
}
</div>
<button className="selectorButton-navButton next" onClick={this.nextClick}><span className="col-white"><i className="fa-solid fa-chevron-right"></i></span></button>
</div>
</>
)
}
}
export default CategorySelector;

Open modal form while retaining a fetch objects properties

const curTodos = useRef({});
const handleClickOpen = (o) => {
console.log(o);
curTodos.current = o;
setOpen(true);
};
const allTodos = todos.map((o) => {
console.log("re-render");
return (
<>
<div key={o.id} className="row">
<span>{o.name}</span>
<span>{o.id}</span>
<span>{o.email}</span>
<span>{o.task}</span>
<Button onClick={() => handleClickOpen(o)} variant="outlined">
Edit Todo
</Button>
</div>
</>
);
});
https://codesandbox.io/s/sweet-platform-du3i8x?file=/src/App.js:1593-1664
I made a different component for my modal
When I click on edit todo I want the todo form modal to contain the name and task that the row is on. Currently it just shows up as an empty input
That is,
currently:
I want:
curTodos is a reference to todo object
When I click on edit todos I want the default value to be set to the one on the rows.
Since its already rendered this wont work it just shows up as empty input.
useState(default) value runs only once on mount. Since you're using a component that does not unmount in this view, you can include an effect to update the form state.
// in FormModal
useEffect(() => {
setName(o.name)
setTask(o.task)
}, [o]);

All button state chnages instead of one button on onclick

I want to change text of only clicked button, like when I'm making shopping cart and I click on "add to cart" button so it should change to "added to cart".
But in my case all buttons text changes to "added to cart" on clicked button. This is because I'm sharing common state for all buttons. But I dont know how to do.
Also, when I refresh my page it again shows "add to cart" text on button. Instead of using redux is there any way to handle this?
addToCart(image, name, type, price, id) {
const found = this.state.selectedProduct.some(el => el.id === id);
const obj = {
image,
name,
type,
price,
id
};
const selectedProduct = found ? this.state.selectedProduct : [...this.state.selectedProduct, obj];
const totalPrice = found ? this.state.totalPrice : this.state.totalPrice + parseInt(price, 10)
this.setState({
selectedProduct,
totalPrice,
isAdded: true
})
localStorage.setItem('total', totalPrice);
localStorage.setItem("items", JSON.stringify(selectedProduct));
}
<button
key={i}
id={i}
style={{backgroundColor:'#f15d24',cursor:"pointer",fontSize:'small',padding:'10px', border:'none', color:'white',float:'right', borderRadius:'5px', fontWeight:'bold'}}
type="button"
onClick={this.addToCart.bind(
this,
publicUrl+item.icon,
item.title,
item.type,
ele.offer,
i
)}
>
{this.state.isAdded ? "Added to Cart" : "Add To Cart"}</button>
After clicking on first item
this.state.isAdded is attached to every unique item so one change in any one item leads to change for all items
make isAdded an object of the key as items id and value as boolean
// in state initialisation
isAdded: {}
// in render()
{this.state.isAdded && this.state.isAdded[i] ? "Added to Cart" : "Add To Cart"}
// in addToCart()
const isAdded = this.state.isAdded;
isAdded[id] = true;
this.setState({
selectedProduct,
totalPrice,
isAdded,
})

React reusable button

React beginner here.
I have a react button component that I would like to reuse across my app.
This is my reusable button component:
const Button = (props) => {
const [buttonPress, setbuttonPress] = useState('0');
const click = () => {
buttonPress !== '1' ? setbuttonPress('1') : setbuttonPress('0');
}
return (
<Button className={buttonPress == '1' ? 'active' : ''} onClick={click}>
{props.children}
</Button>
)
};
On click, it add an 'active' class to the component.
What I can't figure out is how to remove the class of the the previous 'active' button once I click on a different button.
<Button>Dog</Button>
<Button>Cat</Button>
<Button>Horse</Button>
I misunderstood your question, but I think I was able to figure out.
You can check the example here.
In general, you will always need to keep the data inside the parent component and do a validation inside the child component, the data has to be accessible to every button.
You need to store index of pressed button in parent container and update it in function click. You can try this
const Button = (props) => {
return (
<Button className={props.isPress == '1' ? 'active' : ''} onClick={props.click}>
{props.children}
</Button>
)
};
// Parent container
const [buttonPress, setbuttonPress] = useState(null);
const click = index => {
setbuttonPress(index)
}
const buttonList = ['Dog', 'Cat', 'Horse'].map((item, index) => <Button click={click(index)} isPress={buttonPress == index ? '1' : '0'}>{item}</Button>)
I've managed to solve it myself and it works as expected.
I used: useEffect, useCallback, useRef.
https://codesandbox.io/s/buttons-fc6xq?file=/src/index.js

ag-grid react cellRendererFramework doesn't re render when state changed

I'm displaying data in ag-grid-react and grid has come conditional cell rendering based on state. Here is my code. This is working on first run and displaying "YEAH" button. when i clicked button i want to change with NOOO button
This is state
this.changebutton = this.changebutton.bind(this);
this.state= {
isyes = "yes"
}
This is ag-grid-cell-renderer
cellRendererFramework: (params) => {
return <div>
{
this.state.isyes === "yes" ?
<button onClick= {() => this.changebutton()}>YEAH</button>
:
<button onClick= {() => this.changebutton()}>NOOOO</button>
}
</div>
}
this is state changer
changebutton() {
this.setState({isyes: "no" })
console.log(this.state.isyes)
}
I seeing state is changing properly, But doesn't see any change of button. why?
Your code seems incomplete to check your situation. First thing that comes in mind is the expression is evaluated once (maybe, as it appears as a method of an object not directly in render function) and is never retriggered.
Also notiche that setState() is async so you should not:
this.setState({isyes: "no" });
console.log(this.state.isyes);
instead you should:
this.setState(
{isyes: "no" },
() => console.log(this.state.isyes);
);
Try with:
api.refreshCells()
ref: ag-grid.com/javascript-grid-cell-rendering-components

Resources