All button state chnages instead of one button on onclick - reactjs

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

Related

How to handle onClick event to display text after the click

Using react typescript and I’m confused that when I click a button I want some text to appear below the button or at-least anywhere so I made a function to handle the onClick from the button and returned a h1 from the function but turns out no h1 appears on screen after button click. Any idea why?
const handleOnClick=(id:any)=>{
console.log("button clicked" + id)
return(
<h1>Clicked it</h1>
);
}
My Function is this and in another function I have
<button onClick={()=>{handleOnClick(someId)}}>a</button>
I can see the console log but the h1 doesn’t work. Any ideas?
If you think about it, what your handleOnClick doing is returning a bunch of jsx, where do you think these jsx will appear since we didn't specify any location for them? Now if you try something like this:
<button>{ handleOnClick('someId') }</button>
You will see the h1 on the screen because you specify that's where you want to render it, right inside the button element.
A classic way in js to render out something on button click is like this:
const handleOnClick=(e)=>{
// create the element
const newEle = document.createElement('h1');
newEle.innerText = 'Hello';
// append it inside the button
e.target.appendChild(newEle);
}
export default function App() {
const [nameId, setNameId] = useState<String>("");
const handleClick = (id: String) => () => {
console.log("button clicked: ", id);
setNameId(nameId ? "" : id);
};
return (
<>
<button onClick={handleClick("Rohan")}>
{nameId ? "Hide" : "Greet"}
</button>
{!!nameId && <h1>Hello {nameId} Haldiya</h1>}
</>
);
}
When the click is triggered, you need to add the <h1> element into your JSX code, and returning it from the click handler is not enough because you need to tell it where is should be added.
A good way of doing that in React is by using a state which tells you if the button was clicked or not, and if it was, then you display the <h1> element onto the screen. See the code below:
const [isActive, setIsActive] = useState(false);
const handleOnClick = (id) => {
console.log("button clicked" + id);
setIsActive(true);
};
and in your JSX code, below the button, you just need to add the second line of the following:
<button onClick={()=>{handleOnClick(someId)}}>a</button>
{isActive && <h1>Button was clicked.</h1>}
And if you want to toggle the click, So the first time you click the <h1> is showing , but if you click again it disappears, then you could simply do this in the handleOnClick function instead of the above:
const handleOnClick = (id) => {
console.log("button clicked" + id);
setIsActive((prevState) => (prevState === false ? true : false));
};
Hope this helps!

How to undo OnClick function on second click in ReactJS

I have coded a OnClick function that opens a window with a detailed description of the selected object.
However, the window always remains open and to remove it you have to refresh the page. I would like to make sure that at the second click the page returns as before by undoing the function called at the first click.
const setActiveTutorial = (tutorial, index) => {
setCurrentTutorial(tutorial);
setCurrentIndex(index);
};
...
{tutorials &&
tutorials.map((tutorial, index) => (
<TableRow
className={
"list-group-item " + (index === currentIndex ? "active" : "")
}
onClick={() => setActiveTutorial(tutorial, index)}
key={index}
>
<TableCell>{tutorial.title}</TableCell>
<TableCell>{tutorial.size}</TableCell>
<TableCell>{tutorial.country}</TableCell>
...
If you want to use the same function, you can just add a conditional where if currentTutorial already has a value, then it closes the page
const setActiveTutorial = (tutorial, index) => {
if(currentTutorial === tutorial){
setCurrentTutorial(null)
set setCurrentIndex(-1) //or whatever initial value
}
else{
setCurrentTutorial(tutorial);
setCurrentIndex(index);
};
This assumes you can't click on another tutorial while the current one is active.

What is the name of row click event in ag-grid-react reactjs?

If a user click or select a row in ag-grid then I want to fire a click event. I searched the doc but no luck.
It's called onRowClicked and onRowSelected respectively. onRowClicked will be called when you click a row even if you disable row selection. onRowSelected on the other hand only fires when a row is selected
<AgGridReact
columnDefs={columnDefs}
onRowSelected={(e) => console.log("row selected", e.rowIndex)}
onRowClicked={(e) => console.log("row clicked", e.rowIndex)}
rowSelection="multiple"
rowData={rowData}
/>
Another way is to catch all of the ag-grid events using GridApi.addGlobalListener() and filter out the ones you're not interested in.
const onGridReady = (params: GridReadyEvent) => {
params.api.addGlobalListener((type: string, e) => {
if (type === "rowClicked" || type === "rowSelected") {
console.log(type, "from global event handler");
}
});
};
Live Demo

Reactjs hooks set active buttons

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;

Toggling font awesome Icon using React

I want to toggle a fontAwesome icon class name on click. When clicked, the icon should change the color and also call a service which adds an object to a favorite list in the server (hence, why I use e.currentTarget: I need to remember which icon was clicked). This code works on the first click, but fails to change the class back on the second click (doing an inspect, it says the FA´s classname equals "Object object"). Any idea how I could fix it?
<FontAwesomeIcon onClick={this.ToggleClass} size={"sm"} icon={faHeart} />
ToggleClass = (e) => {
const heart = {
color: "#E4002B",
}
const clicked = {
color: "#E4002B",
background: "red"
}
if (e.currentTarget.className.baseVal != heart && e.currentTarget.className.baseVal != clicked) {
return e.currentTarget.className.baseVal === clicked;
#Callservicehere
}
else if (e.currentTarget.className.baseVal === clicked) {
e.currentTarget.className.baseVal = heart;
#callservicehere
}
}
You're not thinking in React yet :)
Accessing the event target and imperatively manipulating the DOM bypasses React's rendering - you might as well just be using jQuery. Not that there's anything bad about that, but it's not the right way to go about things in React.
In React, if you need to change the DOM in response to user interaction you do it in the render method, i.e. output different JSX based on the component's current state or props.
A couple things that might help here:
clicked and heart are both objects which means that you cannot compare them without using a deep comparison method.
var a = { id: 1 }
var b = { id: 1 }
console.log(a == b) //false
console.log(a === b) //false
If you want to compare them, you can convert them both to strings using the toString() method
heart.toString() === clicked.toString()
In your first if condition, it looks like you're returning a true/false value instead of assigning a desired classname to your target.
return e.currentTarget.className.baseVal === clicked // true/false
e.currentTarget.className.baseVal = clicked // assigned
You could also take the approach of keeping your classnames as strings and adding your styled objects inside of css
class MysteryComponent extends React.Component {
state = {
className: 'heart'
}
toggleClass = (e) => {
if (this.state.className === 'heart') {
this.setState({ className: 'clicked' })
} else if (this.state.className === 'clicked') {
this.setState({ className: 'heart' })
}
}
render() {
return (
<div className={this.state.className}>
<FontAwesomeIcon onClick={this.toggleClass} size={"sm"} icon={faHeart} />
</div>
)
}
}
// css
.heart {
color: "#E4002B";
}
.clicked {
color: "#E4002B";
background: "red";
}
I see. You want to fill/unfill the color of the heart as the user clicks. The reason why the results are not meeting your expectations is because of the event.targets are especially funky with FontAwesome. You may think you're clicking on it, but it manipulates the DOM in a way that when you try extract the className, it's value is often inconsistent.
This is why everyone is recommending that you make use of React's state. The logic that determines how elements are styled is now more controlled by the component itself instead of the FontAwesome library. Consider the code below, we only care about whether the item was clicked, not what class it initially has.
class Example extends React.Component{
state = {
clicked: false
}
handleOnCLick = () => {
this.setState({
clicked: !this.state.clicked
})
}
render(){
var clicked = this.state.clicked
return(
<button onClick={this.handleOnClick}>
<i
class={ clicked ? "fas fa-heart" : "fas fa-circle"}
</i>
</button>
)
}
}

Resources