I have button with onClick function like this :
<button className="btn" onClick={(e) => handleClick(user._id, e)} >Edit</button>
const [show, setShow] = useState(false)
const handleClick = (id, e) => {
e.preventDefault()
setShow(!show)
dispatch(getUser(id))}
/* And here rendering based on State */
{
show ? renderUserData() : null
}
So, problem is that setShow(!show) inside the handleClick function does not work and state of show remains false, immutable. if handleClick function contains only setShow(!show) then this function working and toggling renderUserData(), but when it contains both dispatch(getUser(id)) and setShow(!show), state of show remains false all time,please help
try to remove the e.preventDefault() you can play around with your own data but the e.preventDefault() is not useful in buttons (if you have a form try to put it in the onSubmit of the form) but the final solution must look something like this:
import React, { useState } from "react";
export default function App() {
const [show, setShow] = useState(false);
const handleClick = (id, e) => {
setShow(!show);
};
return (
<>
{show ? <h1>Data showed</h1> : null}
<button className="btn" onClick={(e) => handleClick(e)}>
Edit
</button>
</>
);
}
Related
I have two buttons. I can change its color by clicking on one button. And when you click on another button, change its color as well, and return the old color to the first button. Something like toggle. How can I implement such functionality in a react applicatio.
const [toggle, setToggle] = useState(false);
const toggleIt = () => {
setToggle(!toggle);
};
return (
<div>
<button onClick={toggleIt}>Button1</button>
<button onClick={toggleIt}>Button2</button>
)
somthing like this (codesandbox),
import classNames from "classnames";
import { useCallback, useState } from "react";
import "./styles.css";
export default function App() {
const [toggle, setToggle] = useState(false);
const toggleIt = useCallback(() => {
setToggle((toggle) => !toggle);
}, []);
return (
<div>
<button
onClick={toggleIt}
className={classNames({
"btn-act": toggle
})}
>
Btn A
</button>
<button
onClick={toggleIt}
className={classNames({
"btn-act": !toggle
})}
>
Btn B
</button>
</div>
);
}
const [toggle, setToggle] = useState(false);
const toggleIt = () => {
setToggle(!toggle);
};
return (
<div>
<button onClick={toggleIt} style={toggle ? {color: "blue"} : {color: "red"}}</button>
<button onClick={toggleIt} style={toggle ? {color: "pink"} : {color: "purple"}}</button>
</div>
)
Background
You can use the useEffect() hook to accomplish this feature depending on the button pressed. Just hold two states and flip them each time a different button is pressed, and with those two states you can use two separate functions to handle the onClick()'s.
The useEffect() hook automatically re-renders the component once any of the items in the dependency array at the end change, which will happen depending on the button pressed.
You can also directly set true/false values on your state variables with the second value that returns from useState(), and those state variables will automatically have their states updated without you manually assigning them.
There is very likely a better, more efficient way of doing it, but this is just a general guideline, if you will.
This is the code
const [toggleOne, setToggleOne] = useState(false);
const [toggleTwo, setToggleTwo] = useState(true);
const toggleFirst = () => {
setToggleOne(true);
setToggleTwo(false);
};
const toggleSecond = () => {
setToggleOne(false);
setToggleTwo(true);
};
useEffect(() => {
if (toggleOne) {
// Do something with first button pressed
} else if (toggleTwo) {
// Do something with second button pressed
}
}, [toggleOne, toggleTwo]);
return (
<div>
<button onClick={toggleFirst}>Button1</button>
<button onClick={toggleSecond}>Button2</button>
</div>
);
I'm super new to React and building my first ever app which is a url shortening app. Each shortened url has a button next to it whose text is set to 'copy' initially and once the user click on it the link is copied to the clipboard and the button text changes to 'copied'. Everything is working fine except if I have multiple shortened url's and I click on one of the buttons next to any particular url, it still only copies that url to clipboard but the button text changes to copied on all of them.
If anyone can please enlighten me how to single out those buttons individually that'll be of great help. I've tried using the id but maybe I'm not doing that correctly?
P.S - this is first time I'm posting on here so apologies upfront if I missed any crucial bits.
import {useState} from 'react'
import axios from 'axios'
import { v4 as uuidv4 } from 'uuid';
function Main() {
const [name, setName] = useState('')
const [list, setList] = useState(initialList);
const handleSubmit = (e) => {
e.preventDefault();
}
const handleAdd = async () => {
const res = await axios.get(`https://api.shrtco.de/v2/shorten?url=${name}`)
const {data: {result: {full_short_link: shortLink}}} = res
const newList = list.concat({name:shortLink, id: uuidv4()});
setList(newList);
setName('');
}
const [buttonText, setButtonText] = useState("Copy");
return (
<form onSubmit={handleSubmit}>
<input type="text"
value= {name}
onChange={(e) => setName(e.target.value)}
placeholder='Shorten a link here'
onClick = {()=> setButtonText('copy')}
/>
<button onClick = {handleAdd}>Shorten it!</button>
</form>
<ul>
{list.map((item, index) => (
<li key={item.id}>{item.name}<button
onClick = {() => { navigator.clipboard.writeText(item.name); setButtonText("Copied")}} >
{buttonText}
</button></li>))}
</ul>
export default Main``
It’s because you are using one state variable for all of your buttons, you need a variable to keep track of state for each individual button. You should refactor the code within your map function into its own component, and declare the buttonText state within that component. That way each button has its’ own state.
Eg (sorry for the capitalisations in my code):
MyButton.js
Const MyButton = ({item}) => {
const [buttonText, setButtonText] = useState(‘Copy’)
Return (
<li key={item.id}>{item.name}
<button
onClick = {() => {
navigator.clipboard.writeText(item.name);
setButtonText("Copied")}
}
>
{buttonText}
</button>
</li>
)
Export default MyButton
Form:
// ……
<ul>
{list.map((item, index) => <MyButton key={item.id} item={item} />)}
</ul>
To show my question here is a Demo code.(I'm using React Hooks and Antd.)
My Question is:
when currId state is changed and I click MyButton the state is still '' (which is the initial state). onClick event is an arrow function and in it is showModal with params, if there's no params currId can be seen changed but now with params state isn't changed. May I ask what is the reason of it and how I can get changed currId in showModal?
(operation: click 'Change CurrId' button --> setCurrId('12345') ---> click 'MyButton' ---> console.log(currId))
import React, { useState } from 'react'
import 'antd/dist/antd.css';
import { Button} from 'antd';
const MyComponent= () => {
const [currId, setCurrId] = useState('');
const changeCurrId= async () => {
setCurrSolutionId('12345');
}
const showModal = async (num:any) => {
console.log("☆ currid:");// I cannot get the currId state '12345' but ''
console.log(currId);
console.log("☆ num:");//I can get the num params 5
console.log(num);
};
return (
<>
<Button type="primary" onClick={changeCurrId}>Change CurrId</Button>
<Button type="primary" onClick={() => {showModal(5)}}>MyButton</Button>
</>
);
}
const MyComponent= () => {
const [currId, setCurrId] = useState('');
const changeCurrId= () => {
setCurrId('12345');
}
const showModal = (num:string) => {
console.log("☆ currid:");
console.log(num);
};
const changeCurrentIdAndShowModal = (id : string) => {
setCurrId(id);
showModal(id)
console.log("☆ id:");
console.log(id);
};
return (
<>
<Button type="primary" onClick={() => changeCurrId()}>MyButton</Button>
<Button type="primary" onClick={() => showModal('5')}>MyButton</Button>
<Button type="primary" onClick={() => changeCurrentIdAndShowModal('12345')}>MyButton</Button>
</>
);
}
Is this the intention you want?
When I click on a button, I am adding a product to the cart but I also want to set the state of a side drawer to true so it appears. This is working but trying to pass that state to the component so that when I click on close is giving me trouble. Here are basically all the moving parts:
const [isOpen, setIsOpen] = useState(false);
const addToCartHandler = async (id) => {
setIsOpen(true);
// add to cart logic here
}
<CartDrawer toggleOpen={isOpen} />
<Button
variant="primary"
onClick={() => addToCartHandler(id)}
>
Add to Cart
</Button>
This is working fine. I click on add to cart, it adds to cart and my modal shows up as expected.
The modal is basically component and I am receiving toggleOpen as props. Here is the CartDrawer component
const CartDrawer = (props) => {
const [isOpen, setIsOpen] = useState(false);
const closeNavHandler = () => {
setIsOpen(false);
};
return (
<div
id="mySidenav"
className={props.toggleOpen ? "sidenav open" : "sidenav"}
>
<a
className="closebtn"
onClick={closeNavHandler}
>
×
</a>
</div>
);
};
export default CartDrawer;
I know this is wrong but I can't figure out how to update the state here correctly to close it.
Just control everything from the parent. The cartDrawer only needs to receive a ìsOpen prop to know its state. Don't write another state in it.
A component like this should be stupid. It receives informations, and display them. Don't spill the logic all over your components. Just have a single source of truth.
// Main
const [isOpen, setIsOpen] = useState(false);
const addToCartHandler = async (id) => {
setIsOpen(true);
// add to cart logic here
}
<CartDrawer isOpen={isOpen} onClose={()=> setIsOpen(false)}/>
<Button
variant="primary"
onClick={() => addToCartHandler(id)}
>
Add to Cart
</Button>
// CartDrawer
const CartDrawer = ({isOpen, onClose}) => {
return (
<div
id="mySidenav"
className={isOpen ? "sidenav open" : "sidenav"}
>
<a
className="closebtn"
onClick={onClose}
>
×
</a>
</div>
);
};
export default CartDrawer;
I think that's what you want?
const [isOpen, setIsOpen] = useState(false);
const addToCartHandler = async (id) => {
setIsOpen(true);
// add to cart logic here
}
const toggleOpenDrawer = (val) => {
setIsOpen(val);
}
<CartDrawer toggleOpenDrawer={toggleOpenDrawer} toggleOpen={isOpen} />
<Button
variant="primary"
onClick={() => addToCartHandler(id)}
>
Add to Cart
</Button>
const CartDrawer = (props) => {
const { toggleOpenDrawer } = props
return (
<div
id="mySidenav"
className={props.toggleOpen ? "sidenav open" : "sidenav"}
>
<a
className="closebtn"
onClick={toggleOpenDrawer(!props.toggleOpen)}
>
×
</a>
</div>
);
};
export default CartDrawer;
The "Bad button" only works once, since it's not controlling the same "value" as the "Good button". What am I missing here?
const Test = () => {
const [value, setValue] = useState(0)
const [view, setView] = useState()
const add = () => setValue(value + 1)
const badButton = () => {
return (
<div>
<button onClick={add}>Bad button</button>
</div>
)
}
return (
<div>
{value}
<button onClick={add}>Good button</button>
{view}
<button onClick={() => setView(badButton)}>show bad button</button>
</div>
)
}
Thanks for replying, I'm going to use the flag method as suggested. But I would still like to know why the two buttons don't work the same way in this original case.
Check this sandbox
I feel it's the right way to handle such a usecase. Instead of storing component itself in state. I replaced it with a boolean. By default it will be false and badButton will be hidden, on click of showBadButton, i'm setting the view state true and bad button will come into picture. Actually Its a good buton now. Check it out.
I would use view as a flag to show/hide BadButton component, I have created a demo that showcase the following code snippet:
import React, { useState } from 'react';
import { render } from 'react-dom';
import './style.css';
const Test = () => {
const [value, setValue] = useState(0)
const [view, setView] = useState(false)
const add = () => setValue(value + 1)
const BadButton = () => {
return (
<button onClick={add}>Bad button</button>
)
}
return (
<>
{value}
<button onClick={add}>Good button</button>
{view ? BadButton() : null}
<button onClick={() => setView(!view)}>
{view ? 'hide' : 'show'} bad button
</button>
</>
)
}
render(<Test />, document.getElementById('root'));
Welcome to StackOverflow