Adding Two Click Handlers to 1 Function both using React hooks useState - reactjs

I have a button "Add to Cart" and I would like it to do two things when clicked. I want it to add an item to the cart and I also want it to Change the text to "added" for 1 second.
The problem is if I call onClick twice the second function overrides the first.
If I put both click handlers into 1 function and then call that in 1 single onClick the only the function adding things to the cart works.
Where am I going wrong?
const [variant, setVariant] = useState({ ...initialVariant })
const [quantity, setQuantity] = useState(1)
const {
addVariantToCart,
store: { client, adding },
} = useContext(StoreContext)
const handleAddToCart = () => {
addVariantToCart(productVariant.shopifyId, quantity)
}
const text = "Add To Cart";
const [buttonText, setButtonText] = useState(text);
useEffect(() => {
const timer = setTimeout(() => {
setButtonText(text);
}, 1000);
return () => clearTimeout(timer);
}, [buttonText])
const handleClick = () => {
setButtonText("Added");
handleAddToCart();
}
return (
<>
<button
className="add"
type="submit"
disabled={!available || adding}
onClick={handleClick}
>
Add to Cart
</button>
{!available && <p>This Product is out of Stock!</p>}
</>

you need to use the buttonText inside the button as below, however, in your code you have used the hard text Add to Cart.
<button
className="add"
type="submit"
disabled={!available || adding}
onClick={handleClick}
>
{buttonText}
</button>

Related

Set a state inside of a const

I am trying to setAddedToCard(true); when the onClick button is clicked to show that the product is added to the cart but addedToCard is only true after the button is clicked twice. This is because checkItem === true is set upon first click, then retrieved during the second click.
How do I change the code so that addedToCard is true upon first click & maintain checkItem === true if the item was successfully added to the cart?
const [addedToCard, setAddedToCard] = useState(false);
const CampaignAdminAuthToken = localStorage.getItem("CampaignAdminAuthToken");
const user = useSelector((state) => state.user);
const dispatch = useDispatch();
const cart_btn = addedToCard ? (
<Button>
<FontAwesomeIcon icon={solid("circle-check")} />
</Button>
) : (
<Button
onClick={() => {
props.addToCart(productId, totalQuantity);
dispatch(setIsUpdateCart(!user.isUpdateCart));
}}
>
{props.currencySymbol + (totalPrice ? Number(totalPrice) : 0)}
</Button>
);
useEffect(() => {
(async () => {
setTotalPrice(productPrice.toFixed(2));
if (!CampaignAdminAuthToken) {
const checkItem = await props.checkItemInCart(productId);
if (checkItem === true) {
setAddedToCard(true);
} else {
setAddedToCard(false);
}
}
})();
}, [!user.isUpdateCart, productPrice]);

Click event added in useEffect after changing state by onClick is executed in same moment

React 18 changed useEffect timing at it broke my code, that looks like this:
const ContextualMenu = ({ isDisabled }) => {
const [isExpanded, setIsExpanded] = useState(false);
const toggleMenu = useCallback(
() => {
if (isDisabled) return;
setIsExpanded((prevState) => !prevState);
},
[isDisabled],
);
useEffect(() => {
if (isExpanded) {
window.document.addEventListener('click', toggleMenu, false);
}
return () => {
window.document.removeEventListener('click', toggleMenu, false);
};
}, [isExpanded]);
return (
<div>
<div class="button" onClick={toggleMenu}>
<Icon name="options" />
</div>
{isExpanded && <ListMenu />}
</div>
);
};
The problem is, toggleMenu function is executed twice on button click - first one is correct, it's onClick button action, which changes state, but this state change executes useEffect (which adds event listener on click) and this click is executed on the same click, that triggered state change.
So, what should be correct and most "in reactjs spirit" way to fix this?
Your problem is named Event bubbling
You can use stopPropagation to fix that
const toggleMenu = useCallback(
(event) => {
event.stopPropagation();
if (isDisabled) return;
setIsExpanded((prevState) => !prevState);
},
[isDisabled],
);

When a check is checked, a button is shown

I'm using React and I have a list, and each item in the list with a check.
And I want that when there is at least one check in true, a button is shown (it is to delete all the checks).
I am using useReducer() to store the list.
This is my code, but it doesn't work.
const init = () => {
return JSON.parse(localStorage.getItem('todos')) || [];
}
const [todos, dispatch] = useReducer(todoReducer, [], init)
const [todoDelete, setTodoDelete] = useState(0)
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
todos.map(todo => {
todo.check ? setTodoDelete(todoDelete+1) : setTodoDelete(todoDelete-1);
})
}, [todos])
returm (
code...
{
(todoDelete!==0)
&& <button
className='btn btn-danger btn-sm h-50' onClick={handleDeleteChecked}>
Delete
</button>
}
)
What interests me is that when there is more than one check, a button is added, otherwise it is removed. How do i do it?
Change this
todo.check ? setTodoDelete(todoDelete+1) : setTodoDelete(todoDelete-1);
To this
todo.check ?? setTodoDelete(prev => prev+1);

How to disable button based on state of other button

I'm trying to create 2 buttons in react app using material ui buttons with both buttons are enabled at the start of page load. When onClick on one of the button, the other button should be disabled vice versa.
Initial state
When onClick
const [btn1, setBtn1] = useState(false);
const [btn2, setBtn2] = useState(false);
const onBtn1 = () => {
setBtn1(!btn1);
};
const onBtn2 = () => {
setBtn2(!btn2);
};
}
How do i go about doing it? is there anyway to just use a single useState hook to handle 2 state buttons?
You can achieve this with only one state variable and one function
Code
const [disabledButton, setDisabledButton] = useState('');
const onButtonClick = (param) => {
setDisabledButton(param);
}
<Button onClick={() => onButtonClick('btn2')} disabled={disabledButton === 'btn1'}>
Button 1
</Button>
<Button onClick={() => onButtonClick('btn1')} disabled={disabledButton === 'btn2'}>
Button 2
</Button>
You can just enable the other button when a button is clicked:
const onBtn1 = () => {
setBtn1(prevState => !prevState);
setBtn2(false);
};
const onBtn2 = () => {
setBtn2(prevState => !prevState);
setBtn1(false);
};
in the JSX:
<button onClick={onBtn1} disabled={btn1}>btn1</button>
<button onClick={onBtn2} disabled={btn2}>btn2</button>
Change the state of other button on press.
const [btn1, setBtn1] = useState(true); //Initially both buttons are enabled
const [btn2, setBtn2] = useState(true);
const onBtn1 = () => {
setBtn1(!btn1);
setBtn2(false);
};
const onBtn2 = () => {
setBtn2(!btn2);
setBtn1(false);
};
}
you can use a single state, please refer the suggestion below:
state and event handlers-
const [activeButton, setActiveButton] = useState("None");
const onBtn1 = () => {
setActiveButton("Button1");
};
const onBtn2 = () => {
setActiveButton("Button2");
};
HTML Element part -
<Button disabled={!['None', 'Button1'].includes(activeButton)}>Button1</Button>
<Button disabled={!['None', 'Button2'].includes(activeButton)}>Button2</Button>

setState inside setTimeout react hooks

I am currently trying to build a rock-paper-scissor and what I intend to achieve are this logic:
after the start button clicked, a player has 3seconds to pick a weapon, if not, a random weapon will be picked for the player.
The problem:
When I picked a weapon under the 3seconds, it works just fine. But, when I intentionally let the setTimeout triggered, it is not updating the state automatically. I suspected the if conditions are not met, but I don't know why that happen.
Here is the code snippet:
//custom hooks//
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const weapons= ['rock', 'weapon', 'scissors']
const App = () => {
const [p1Weapon, setp1Weapon] = useState("");
const prevWeapon = usePrevious(p1Weapon);
const getPlayerTimeout = (playerRef, setPlayer, time) => {
setTimeout(() => {
if (playerRef === "") {
setPlayer(weapons[Math.floor(Math.random() * weapons.length)]);
}
}, time);
};
const startGame = () => {
getPlayerTimeout(prevWeapon, setp1Weapon, 3000);
}
return (
...
<div>
<button
className="weaponBtn"
onClick={() => {
setp1Weapon("rock");
}}
>
rock
</button>
<button className="weaponBtn" onClick={() => setp1Weapon("paper")}>
paper
</button>
<button className="weaponBtn" onClick={() => setp1Weapon("scissors")}>
scissor
</button>
<button type="button" onClick={startGame}>
Start!
</button>
</div>
)
Thanks!
if all you want to do is set a state after x time you can do this very easily like this
this.setState({isLoading: true},
()=> {window.setTimeout(()=>{this.setState({isLoading: false})}, 8000)}
);
this should set the value of isLoading to false after 8 seconds.
I hope it helps

Resources