So the scenario is I have a React + Redux ECommerce store and there is a Checkout Page where all the items in the cart are listed. This works fine, the buttons get re-rendered every time I add a product, however, there is the option to delete items from the cart on the checkout page, and although the actual state changes, the Paypal buttons use the old state (I suppose because the buttons don't get re-rendered). I have wrapped the buttons in a useEffect function that should run every time there is a state change with the items in the cart, however, this is not the case and I just can't figure out why.
Here is the code:
import {useSelector, useDispatch} from 'react-redux'
import {removeProduct} from '../redux/cart/cart.actions'
import './Cart.scss'
import CartItem from '../components/cartComponent/cartItem'
const mapState = ({ cart }) => ({
itemsInCart: cart.itemsInCart,
total: cart.total
})
const Cart = props => {
const [paidFor, setPaidFor] = useState(false);
const dispatch = useDispatch();
const {itemsInCart} = useSelector(mapState);
const {total} = useSelector(mapState);
let paypalRef = useRef();
const clickHandler = (e) => {
e.preventDefault();
const removeIndex = itemsInCart.findIndex(item => {
return item.id === e.target.id
})
dispatch(removeProduct(removeIndex,itemsInCart[removeIndex]))
}
useEffect(() => {
window.paypal
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: "product.description",
amount: {
currency_code: 'USD',
value: total,
},
},
],
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
setPaidFor(true);
console.log(order);
},
onError: err => {
setError(err);
console.error(err);
},
})
.render(paypalRef.current);
},[itemsInCart]);
return (
<>
{paidFor ? (
<div><h1>Congrats you just bought an Article</h1></div>
):
<>
<h1>Your Shopping Cart</h1>
<div className = "main">
<form className="cart-area">
<ul className="cart-ui">
{itemsInCart.map((item, index)=> {
return <CartItem
clicked={clickHandler}
name={item.name}
price={item.price}
id={item.id}
key={index}
/>
})}
</ul>
<div className="cart-summary">
<div className ="cart-wrapper">
<div className="name-price-wrapper">
<p className="name">Items</p>
<p className="price">{total}€</p>
</div>
<div ref={paypalRef}></div>
</div>
</div>
</form>
</div>
</>
}
</>
)
}
export default Cart;
When I change the useEffect hook to e.g. always rerender, I get the buttons multiple time (depending on how many items I delete) and when clicking the button on the bottom it actually works with the right amount. So I have to close the previous buttons because they get rendered.
When following the referenced post from @Preston PHX my code looks like:
useEffect(() => {
if(window.myButton) window.paypal.close();
window.myButton = window.paypal
.Buttons({
createOrder: (data, actions) => {
console.log(total)
return actions.order.create({
purchase_units: [
{
description: "product.description",
amount: {
currency_code: 'USD',
value: total,
},
},
],
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
setPaidFor(true);
console.log(order);
},
onError: err => {
setError(err);
console.error(err);
},
})
.render(paypalRef.current)
});
Here I get the error window.myButton.close() is not a function ...?