I am rendering a map of buttons in react to toggle the is-active class. But if i click one of them, all of them are opened. How can i check if only the clicked button toggles the class?
In plain javascript something like this feels much easier. thanks guys!
const [open, setOpen] = useState(false)
<div
id={"id" + node.id}
key={node.id}
className={`dropdown ${open ? "is-active" : ""}`}
onClick={() => setOpen(!open)}
>
Of course all will be active. You need to place an id to state in order to check which button is clicked.
<div
className="dropdown"
id={"id" + node.id}
key={node.id}
className={`dropdown ${open === node.id ? "is-active" : ""}`}
onClick={() => setOpen(node.id)}
>
You can extract your button to component and change it local state when button is clicked.
function Button(props) {
const [open, setOpen] = useState(false)
return (
<div
className="dropdown"
id={"id" + node.id}
key={node.id}
className={`dropdown ${open ? "is-active" : ""}`}
onClick={() => setOpen(!open)}
>MyButton</div>
)
}
Related
On clicking an "add to basket" button that specific button should get replaced by - and + buttons.
Right now all of the mapped buttons are getting changed at the same time.
How to inflict change only on one "add to basket" button?
Using: React, Chakra UI buttons, and the .map function.
{products.map((item) => {
return (
<Box key={item.id} border="1px solid black" h="auto" >
<Link to={`/product/single-product/${item.id}`}>
<Box h="auto" border="1px solid black">
<Box><Image src={item.image} h="fit-content" /></Box>
<Text h="fit-content" >{item.name}</Text>
<Text h="fit-content" >Rs.{item.price}</Text>
</Box>
</Link>
<Box align="center" border="1px solid black" h="50px" >
{ addToBasket && <Button onClick={() => handleAddToBasket(item)} ml="5px" >ADD TO BASKET</Button> }
{ addBtn && <Button ml="5px" onClick={handleReduceItem} >-</Button> }
{ addBtn && <Button ml="5px" >+</Button> }
</Box>
</Box>
)})}
button onClick functions are here:
const [addToBasket, setAddToBasket] = useState(true);
const [addBtn,setAddBtn] = useState(false);
function handleAddToBasket(singleProduct){
if(addToBasket === true){
setAddToBasket(false)
setAddBtn(true);
}
dispatch(addToCart(singleProduct))
}
function handleReduceItem(){
if(addBtn === true){
setAddToBasket(true)
setAddBtn(false);
}
}
This solution only for add To Basket button.
Note: You can utilize your Redux store state. I'm using local state only.
First, You are using addToBasket state for all products and it's boolean value. So whenever you change the state of addToBasket is effects in all products.
Change this Boolean to an array.
const [basket, setAddToBasket] = useState([]);
In handleAddToBasket make this changes.
const [basket, setAddToBasket] = useState([]);
const [addBtn,setAddBtn] = useState(false);
function handleAddToBasket(singleProduct){
if(!basket.includes(singleProduct.id)){
setAddToBasket((ids) => [...ids, singleProduct.id])
setAddBtn(true);
dispatch(addToCart(singleProduct))
} else {
const newBasket = basket.filter((id) => id !== singleProduct.id)
setAddToBasket(newBasket)
// call your remove from cart dispatch here or however you want to handle it
}
}
In jsx
<Button onClick={() => handleAddToBasket(item)} ml="5px" >{basket.includes(item.id) ? 'ADDED' : 'ADD TO BASKET' }</Button>
I think instead of just hiding the button you should display a text ADDED for already then you can toggle the item in your cart.
I have a 'RadioItem' component which applys a 'selected' class via an 'isChecked' boolean prop when it's clicked:
{categoryFilter.map((item) => {
return (
<RadioItem
isChecked={selectedItem.id === item.id}
key={item.id}
itemName={item.name}
itemPrice={item.price}
onClick={() => clickHandler(item)}
/>
);
})}
const RadioItem = (props) => {
const clickHandler = () => {
props.onClick();
};
return (
<li
className={props.isChecked ? "item selected" : "item"}
onClick={clickHandler}
>
<div className="item__body">
<div className="item__radio"></div>
<h3 className="item__name">{props.itemName}</h3>
</div>
{props.itemPrice !== 0 && (
<p className="item__price">£{props.itemPrice}</p>
)}
</li>
);
};
I'd like to apply this 'selected' class to the first item (i.e. where the index is 0) when the page first loads but for it to then be deselected when another item is clicked. I have tried, for example, applying an OR condition to the isChecked, like this:
isChecked={selectedItem.id === item.id || index === 0}
but then of course the selected class persists on that first item regardless of me clicking other items.
Any help greatly appreciated, thanks.
Hi I'm new to ReactJS so I'm importing some work of mine to my project and turning it into components. I have a span acting as a button with keyframe animations, but when i load the page it gets animated.
One solution i have is to make a .class.animation and give the .animation class on the first click.
The thing is that I know how to code it in jquery but not on react.
The idea is to convert this into react:
$('.navTrigger').onClick(function () {
$(this).addClass('animations');
});
Right now I have this:
const [isActive, setActive] = useState("false");
const handleToggle = () => {
setActive(!isActive);
};
return (
<span onClick={handleToggle} className={`navTrigger ${isActive ? "" : "animations"} ${isActive ? "" : "active"}`}>
<i></i>
<i></i>
<i></i>
</span>
);
I have a function where i toggle the active class to run backward and forward animations.
What the code below does is on web load, the span starts with navTrigger class. The first time you click it its's given animations and active class. And then if you interact with it again it takes out active class and leaves animations there
function MenuButton() {
const [isActive, setActive] = useState(false);
const [animating, setAnimating] = useState(false);
const handleToggle = () => {
setActive(!isActive);
setAnimating(true);
};
return (
<span onClick={handleToggle} className={`navTrigger ${animating ? "animations" : ""} ${isActive ? "active" : ""}`}>
<i></i>
<i></i>
<i></i>
</span>
);
}
I'm having accordion component which is revealed on click. I have an issue with closing it on second click. Currently I'm clicking once and it expands but after second click nothing changes. Thank you for help
const [activeTab, setActiveTab] = React.useState(props.tabIndex);
const tabs = props.tabs.map((tab) => {
return (
<>
<TabContainer>
<Tab
key={tab.index}
onClick={() => setActiveTab(tab.index)}. //here it opens
className={typeof tab.content === 'string' ? '' : 'unactive-tab'}
>
{tab.name}
</Tab>
</TabContainer>
{tab.index === activeTab ?
<div
id="content"
style={{ width: '100%', margin: '2rem 0' }}
dangerouslySetInnerHTML={{ __html: tab.content as string }}
/>
: null}
</>
);
});
Maybe try changing your onClick method to this
onClick= {() => {
activTab == tab.index ? setActiveTab(-1) : setActiveTab(tab.index)
})
In the case where active tab matches the current index of the tab clicked, we set it to -1, hence matching no tab ( showing no tab ); else we set the active tab to the current tab clicked.
I'm trying to create a Trivia app in React, but I'm having trouble changing the background color after the user selected the answer from the given options. I'm trying to do the freeCodeCamp's quiz app with some personal changes.
What it currently does is that it colors every single button green or red depending if the answer was correct or wrong and I would like to target only the specific button that has been clicked with the backgroundColor change and leave the rest as they were.
So, here I have a div where i map out the answer options on different buttons:
<div className='answer-Section'>
{currentQuestion.answerOptions.map((elem) => (
<button
className='answer-Button'
onClick={() => handleAnswerClick(elem.isCorrect)}
style={{
backgroundColor: isAnswered ? bgAnswered : bgNeutral,
}}
>
{elem.answerText}
</button>
))}
</div>
And here is the onClick event's handler on the mapped buttons:
const handleAnswerClick = (isCorrect) => {
setIsAnswered(true)
isCorrect ? setScore(score + 1) : setScore(score)
isCorrect ? setBgAnswered('green') : setBgAnswered('red')
setTimeout(() => findNextQuestion(), 1000)}
const findNextQuestion = () => {
setIsAnswered(false)
const nextQuestion = currentQuestionNumber + 1
if (nextQuestion === questions.length) {
setShowScore(true)
} else {
setCurrentQuestionNumber(nextQuestion)
}}
There are a couple of ways to do this. The simplest would be to store not just whether the question was answered, but which answer was chosen. In the code below, I've also avoided using the bgAnswered state variable, instead just computing the desired color inline.
[answer, setAnswer] = useState();
//...
<div className='answer-Section'>
{currentQuestion.answerOptions.map((elem, i) => (
<button
className='answer-Button'
onClick={() => handleAnswerClick(elem.isCorrect, i)}
style={{
backgroundColor: answer === i ? (elem.isCorrect ? 'green' : 'red') : bgNeutral,
}}
>
{elem.answerText}
</button>
))}
</div>
const handleAnswerClick = (isCorrect, i) => {
setAnswer(i);
if (isCorrect) setScore(score + 1);
setTimeout(() => findNextQuestion(), 1000)
};
Another option would be to make bgAnswered into an array of values, one for each answer. But that seems unnecessary in this case.