Reputation: 335
I'm currently working on the checkout page of an application where a user can purchase up to three items at one of three prices chosen by the user (this is mostly being done as an experiment). When the user chooses a price by clicking a button this triggers a setState and a new price is stored to the state. When doing console.log I see the new state has been set, but upon checkout it appears the state resets to its initial value. I can't tell why and have no idea where to begin on this one. I imagine on initial render paypal is keeping the initial state it was passed and needs to be rerendered when the new state is set, but not sure how to go about this or even if this is the problem. Any help or guidance is appreciated.
I'm using the @paypal/react-paypal-js
library for this paypal implementation, but am welcome to alternative suggestions.
Here is the code I'm using but cut down relevant sections:
import React, {useState, useRef, useEffect} from 'react';
import { PayPalButtons, usePayPalScriptReducer } from "@paypal/react-paypal-js";
import PriceButton from './PriceButton.jsx';
import NumberItemButton from './NumberItemButton';
import {priceOptions, amountItems} from './PriceOptions';
const PaymentPage = () => {
const [isLoading, setIsLoading] = useState(false);
const [payAmount, setPayAmount] = useState('5.00');
const [itemAmount, setItemAmount] = useState('1');
const payPalOptions = { //Note: This is used in the higher level component PayPalScriptProvider
"client-id": `${process.env.REACT_APP_PAYPAL_CLIENT_ID}`,
currency: "USD",
intent: "capture",
};
const createOrder = (data, actions) => { //This will show the initial state when triggered
return actions.order.create({
purchase_units : [
{
amount: {
value: payAmount //This stays at the initial State of '5.00' despite the newState being set
}
}
]
})
};
const onApprove = (data, actions) => {
return actions.order.capture().then(function(orderData) {
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
let transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction '+ transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
}
)};
const onError = (error) => {
console.log(error)
}
console.log(payAmount) //Note: This will show the new State
return (
<div>
<h1>Purchase</h1>
<label> Choose number of items
<div>
{amountItems.map((item, index) => {
return <NumberItemButton key={index} setItemAmount={setItemAmount} amount={item.amount} />
})}
</div>
</label>
<label> Pick a price
<div>
{priceOptions.map((item, index) => {
return <PriceButton key={index} itemAmount={itemAmount} setPayAmount={setPayAmount} price={item.price} />
})}
</div>
</label>
<PayPalButtons
createOrder={(data, actions) => createOrder(data, actions)}
onApprove={(data, actions) => onApprove(data, actions)}
onError={onError}
/>
</div>
);
}
export default PaymentPage;
I'll also add the price button component incase the issue is there
const PriceButton = ({itemAmount, setPayAmount, price}) => { //itemAmount is the amount customer buys, price is the value passed through on the mapping function
const multPrice = (itemAmount * price).toFixed(2);
const withTaxPrice = (parseInt(multPrice) + .5).toFixed(2).toString();
return (
<button onClick={() => setPayAmount(withTaxPrice)}>${multPrice}</button>
)
}
export default PriceButton;
Appreciate any help!
Upvotes: 6
Views: 1882
Reputation: 998
Use the forceReRender
property.
Check out the official Storybook: https://paypal.github.io/react-paypal-js/?path=/docs/example-paypalbuttons--default
<PayPalButtons
style={style}
disabled={false}
forceReRender={[amount, currency, style]} /* <= forceReRender */
fundingSource={undefined}
createOrder={(data, actions) => {
return actions.order
.create({
purchase_units: [
{
amount: {
currency_code: currency,
value: amount,
},
},
],
})
.then((orderId) => {
// Your code here after create the order
return orderId;
});
}}
onApprove={function (data, actions) {
return actions.order.capture().then(function () {
// Your code here after capture the order
});
}}
/>
Upvotes: 2
Reputation: 41
I found a better solution. Just use useRef
and access the ref.current
value!
Upvotes: 4
Reputation: 335
I came back to this with a fresh pair of eyes and found the solution (though I'm not sure if it's the best one).
The issue is when the Paypal button renders it pulls in the initial state that is passed through, but it needs to be rerendered when a new state is passed.
My solution to this was to pass a forceReRender={[payAmount]}
within the PaypalButtons component. This rerenders the Paypal button upon update to the price state and allows me to pass an updated value.
Hope this helps others!
Upvotes: 14