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>
);
Related
So I have two radio button images, one checked and one not. I am trying to persist the change of state to view the corresponding image on button click for each of the inputs.
Please help.
Here's my code:
import React, { useState, useEffect } from 'react';
const Option = (props) => {
const img1 = <img alt='' src='/radio-active.png' className='radio__img' />;
const img2 = <img alt='' src='/radio-inactive.png' className='radio__img' />;
const [state, setState] = useState(false);
const handleStateChange = () => {
state === true ? setState(false) : setState(true);
};
useEffect(() => {
setState(JSON.parse(window.localStorage.getItem('state')));
}, []);
useEffect(() => {
window.localStorage.setItem('state', state);
}, [state]);
return (
<div className='option'>
<div className='radio'>
<button className='radio__button' onClick={handleStateChange}>
{state ? img1 : img2}
</button>
<p className='option__text radio__text'>{props.optionText}</p>
</div>
<button
className='button button--link'
onClick={(e) => {
props.handleDeleteOption(props.optionText);
}}
>
remove
</button>
</div>
);
};
export default Option;
All of your Option components are saving the state using the same key ("state"). You'll want each Option to have its own saved state. For each Option, add a new "optionName" property that is the key you want to use when saving the option's value to local storage.
// Change these:
window.localStorage.setItem('state', state);
setState(JSON.parse(window.localStorage.getItem('state')));
// To these:
window.localStorage.setItem(props.optionName, state);
setState(JSON.parse(window.localStorage.getItem(props.optionName)));
I currently have my toggle action in place but the only help I need is that, I would like to close the div as well, like an toggle action. The one that I've currently done is that once I click on another div element the previous one that has been clicked closes, but I'd rather prefer that I have an toggle action on closing and opening on the div element being clicked, without needing to click on another just to close the previous div, I've only grabbed the parts that are needed in the code, just to prevent on copying and pasting the whole file, just to save time on reading.
Code Snippet
const [posts, setPosts] = useState([]);
const [commentState, commentChange] = useState({
activeObject: null
});
const toggleComment = (index) => {
commentChange({...commentState, activeObject: posts[index]})
}
const toggleActiveStyles = (index) => {
if(posts[index] === commentState.activeObject) {
return "dashboard__commentContent toggle";
} else {
return "dashboard__commentContent";
}
}
return error ? (
<span>{error}</span>
) : (
{posts.map((post, i) => (
<button onClick={() => toggleComment(i)} >toggle</button>
<div className={toggleActiveStyles(i)}>
<h1>{post.title}</h1>
</div>
)}
Here is a working codesandbox that you can manipulate to fit to your needs.
Explanation
You would want to keep track of toggled divs and make sure to adjust your class based on that. You can filter out or add to the toggled divs state variable, and do whatever you want while rendering.
Code
import { useState } from "react";
import "./styles.css";
const DATA = ["1", "2", "3", "4"];
export default function App() {
const [closedDivs, setClosedDivs] = useState([]);
const toggleDiv = (i) => {
setClosedDivs((divs) =>
divs.includes(i) ? divs.filter((d) => d !== i) : [...divs, i]
);
};
return (
<div className="App">
{DATA.map((d, i) => (
<div
className={`${closedDivs.includes(i) ? "close" : ""} box`}
onClick={() => toggleDiv(i)}
>
<p> {d} </p>
</div>
))}
</div>
);
}
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>
</>
);
}
I have one simple app that include 3 identical button and when I click the button, onClick event should trigger to display one span. for now, I have use one one state to control span show or not and once I click any one of button all span show. How can I implement the code, so when I click the button, only the correspond span display
import "./styles.css";
import React, { useState } from "react";
const Popup = (props) => {
return <span {...props}>xxx</span>;
};
export default function App() {
const [isOpen, setIsOpen] = useState(true);
const handleOnClick = () => {
setIsOpen(!isOpen);
};
return (
<div className="App">
<button onClick={handleOnClick}> Show popup1</button>
<Popup hidden={isOpen} />
<button onClick={handleOnClick}> Show popup2</button>
<Popup hidden={isOpen} />
<button onClick={handleOnClick}> Show popup3</button>
<Popup hidden={isOpen} />
</div>
);
}
codesandbox:
https://codesandbox.io/s/cocky-fermi-je8lr?file=/src/App.tsx
You should rethink how the components are used.
Since there is a repeating logic and interface, it should be separated to a different component.
const Popup = (props) => {
return <span {...props}>xxx</span>;
};
interface Props {
buttonText: string
popupProps?: any
}
const PopupFC: React.FC<Props> = (props) => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>{props.buttonText}</button>
<Popup hidden={isOpen} {...props.popupProps} />
</>
)
}
export default function App() {
const [isOpen, setIsOpen] = useState(true);
const handleOnClick = () => {
setIsOpen(!isOpen);
};
return (
<div className="App">
<PopupFC buttonText="Show popup1" />
<PopupFC buttonText="Show popup2" />
<PopupFC buttonText="Show popup3" />
</div>
);
}
If each Popup needs its own isOpen state, it would not be possible to achieve with a single boolean state.
Perhaps converting both the button and the span to a single component and letting each Popup component handle its own isOpen:
import "./styles.css";
import React, { useState } from "react";
const Popup = (props) => {
const [isOpen, setIsOpen] = useState(true);
const handleOnClick = () => {
setIsOpen(!isOpen);
};
return (
<>
<button onClick={handleOnClick}>{props.children}</button>
{isOpen && <span {...props}>xxx</span>}
</>
);
};
export default function App() {
return (
<div className="App">
<Popup>Show popup 1</Popup>
<Popup>Show popup 2</Popup>
<Popup>Show popup 3</Popup>
</div>
);
}
That happens simply because you are using the same state "isOpen" for all buttons,
once you click any one of them it reflects all buttons because it's the same value.
you could solve this using Custom Hook since you repeat the logic or you could separate them into small components
Based on your comment, you only want one popup to be open at a time. That was not clear in your original question so the other answers don't address this.
Right now you are just storing a value of isOpen that is true or false. That is not enough information. How do you know which popup is open?
If you want to show just one at a time, you can instead store the number or name (any sort of unique id) for the popup which is currently open.
We make the Popup a "controlled component" where instead of managing its own internal isOpen state, it receives and updates that information via props.
The App component is responsible for managing which popup is open and passing the right props to each Popup component. Since we are doing the same thing for multiple popups, I moved that logic into a renderPopup helper function.
Popup
interface PopupProps {
isOpen: boolean;
open: () => void;
close: () => void;
label: string;
}
const Popup = ({ isOpen, open, close, label }: PopupProps) => {
return (
<>
<button onClick={open}> Show {label}</button>
{isOpen && (
<div>
<h1>{label}</h1>
<span>xxx</span>
<button onClick={close}>Close</button>
</div>
)}
</>
);
};
App
export default function App() {
// store the label of the popup which is open,
// or `null` if all are closed
const [openId, setOpenId] = useState<string | null>(null);
const renderPopup = (label: string) => {
return (
<Popup
label={label}
isOpen={openId === label} // check if this popup is the one that's open
open={() => setOpenId(label)} // open by setting the `openId` to this label
close={() => setOpenId(null)} // calling `close` closes all
/>
);
};
return (
<div className="App">
{renderPopup("Popup 1")}
{renderPopup("Popup 2")}
{renderPopup("Popup 3")}
</div>
);
}
Code Sandbox
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