如何在不重新渲染的情况下将动态金额传递给 Paypal React SDK Button
How to pass dynamic amount to Paypal React SDK Button without re-render
我正在使用 react-paypal-js
,需要在点击 PayPal 按钮时设置一个动态金额。
问题:createOrder
函数无法从我的状态中获取数据(可能是因为它在 PayPal 脚本上下文中)。
无效示例:
const [amount, setAmount] = useState('');
const handleCreatePaypal = (data, actions) => {
const orderAmount = parseFloat(amount).toFixed(2) || '1.00'; //amount is the state
console.log(amount, orderAmount)
return actions.order.create({
purchase_units: [
{ amount: { value: orderAmount } }
]
})
}
return (
<PayPalScriptProvider options={paypalOptions}>
<PayPalButtons
createOrder={(data, actions) => handleCreatePaypal(data, actions)}
onApprove={(data, actions) => handlePaypalApprove(data, actions)}
onError={handlePaymentError}
/>
</PayPalScriptProvider>
)
不完美的解决方案:
- 从HTML获取金额,但我真的不想那样做。
const amountRef = useRef(null); //ref on input not showing here
...
const orderAmount = parseFloat(amountRef.current.value).toFixed(2) || '1.00'; //get value from html
...
- 当数量改变时强制重新渲染。但这将导致按钮消失并在金额更改时再次显示。
<PayPalScriptProvider options={paypalOptions}>
<PayPalButtons
createOrder={(data, actions) => handleCreatePaypal(data, actions)}
forceReRender={[amount]} //force re-render when amount changes
/>
</PayPalScriptProvider>
有解决这个问题的方法吗?提前致谢!
如果没有解决方案,我可能需要从后端集成:(
我不想让它太复杂,因为它只是一个简单的捐赠按钮。
一般来说,从后端集成是最强大的解决方案,尤其是对于电子商务而言——但由于这是一个简单的捐赠用例,我知道您可能希望如何避免这种情况。
有多种可能的解决方案,包括使用额外的状态框架,例如 Redux,但您似乎想要做的是在按钮上方的父组件级别管理状态,并且 bind
它的 createOrder (“handleCreatePayPal”)方法等,以便 this
在上下文中并且它们可以访问 its 状态。
Here is a runnable codesandbox example (based on the documented example that uses a re-render,不过none这里就完成了)
import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";
import * as React from "react";
const debug = true;
const initialState = {
amount: "2.00",
orderID: "",
onApproveMessage: "",
onErrorMessage: ""
};
export default class App extends React.Component<{}, typeof initialState> {
constructor(props: any) {
super(props);
this.state = initialState;
this.onChange = this.onChange.bind(this);
this.createOrder = this.createOrder.bind(this);
this.onApprove = this.onApprove.bind(this);
this.onError = this.onError.bind(this);
this.onClick = this.onClick.bind(this);
}
onChange(event: React.ChangeEvent<HTMLSelectElement>) {
this.setState({
amount: event.target.value,
orderID: "",
onApproveMessage: "",
onErrorMessage: ""
});
}
createOrder(data: Record<string, unknown>, actions: any) {
if (debug) console.log("Creating order for amount", this.state.amount);
return actions.order
.create({
purchase_units: [
{
amount: {
value: this.state.amount
}
}
]
})
.then((orderID: any) => {
this.setState({ orderID: orderID });
return orderID;
});
}
onApprove(data: any, actions: any) {
let app = this;
return actions.order.capture().then(function (details: any) {
app.setState({
onApproveMessage: `Transaction completed by ${details.payer.name.given_name}!`
});
});
}
onError(err: Record<string, unknown>) {
this.setState({
onErrorMessage: err.toString()
});
}
onClick() {
if (debug) console.log("When clicked, amount was", this.state.amount);
}
render() {
return (
<div style={{ minHeight: "300px" }}>
<table className="table" style={{ maxWidth: "400px" }}>
<tbody>
<tr>
<th>
<label htmlFor="amount">Order Amount: </label>
</th>
<td>
<select onChange={this.onChange} name="amount" id="amount">
<option value="2.00">.00</option>
<option value="4.00">.00</option>
<option value="6.00">.00</option>
</select>
</td>
</tr>
<tr>
<th>Order ID:</th>
<td>{this.state.orderID ? this.state.orderID : "unknown"}</td>
</tr>
<tr>
<th>On Approve Message: </th>
<td data-testid="message">{this.state.onApproveMessage}</td>
</tr>
<tr>
<th>On Error Message: </th>
<td data-testid="error">{this.state.onErrorMessage}</td>
</tr>
</tbody>
</table>
<PayPalScriptProvider options={{ "client-id": "test" }}>
<PayPalButtons
createOrder={this.createOrder}
onApprove={this.onApprove}
onError={this.onError}
onClick={this.onClick}
/>
</PayPalScriptProvider>
</div>
);
}
}
我正在使用 react-paypal-js
,需要在点击 PayPal 按钮时设置一个动态金额。
问题:createOrder
函数无法从我的状态中获取数据(可能是因为它在 PayPal 脚本上下文中)。
无效示例:
const [amount, setAmount] = useState('');
const handleCreatePaypal = (data, actions) => {
const orderAmount = parseFloat(amount).toFixed(2) || '1.00'; //amount is the state
console.log(amount, orderAmount)
return actions.order.create({
purchase_units: [
{ amount: { value: orderAmount } }
]
})
}
return (
<PayPalScriptProvider options={paypalOptions}>
<PayPalButtons
createOrder={(data, actions) => handleCreatePaypal(data, actions)}
onApprove={(data, actions) => handlePaypalApprove(data, actions)}
onError={handlePaymentError}
/>
</PayPalScriptProvider>
)
不完美的解决方案:
- 从HTML获取金额,但我真的不想那样做。
const amountRef = useRef(null); //ref on input not showing here
...
const orderAmount = parseFloat(amountRef.current.value).toFixed(2) || '1.00'; //get value from html
...
- 当数量改变时强制重新渲染。但这将导致按钮消失并在金额更改时再次显示。
<PayPalScriptProvider options={paypalOptions}>
<PayPalButtons
createOrder={(data, actions) => handleCreatePaypal(data, actions)}
forceReRender={[amount]} //force re-render when amount changes
/>
</PayPalScriptProvider>
有解决这个问题的方法吗?提前致谢!
如果没有解决方案,我可能需要从后端集成:(
我不想让它太复杂,因为它只是一个简单的捐赠按钮。
一般来说,从后端集成是最强大的解决方案,尤其是对于电子商务而言——但由于这是一个简单的捐赠用例,我知道您可能希望如何避免这种情况。
有多种可能的解决方案,包括使用额外的状态框架,例如 Redux,但您似乎想要做的是在按钮上方的父组件级别管理状态,并且 bind
它的 createOrder (“handleCreatePayPal”)方法等,以便 this
在上下文中并且它们可以访问 its 状态。
Here is a runnable codesandbox example (based on the documented example that uses a re-render,不过none这里就完成了)
import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";
import * as React from "react";
const debug = true;
const initialState = {
amount: "2.00",
orderID: "",
onApproveMessage: "",
onErrorMessage: ""
};
export default class App extends React.Component<{}, typeof initialState> {
constructor(props: any) {
super(props);
this.state = initialState;
this.onChange = this.onChange.bind(this);
this.createOrder = this.createOrder.bind(this);
this.onApprove = this.onApprove.bind(this);
this.onError = this.onError.bind(this);
this.onClick = this.onClick.bind(this);
}
onChange(event: React.ChangeEvent<HTMLSelectElement>) {
this.setState({
amount: event.target.value,
orderID: "",
onApproveMessage: "",
onErrorMessage: ""
});
}
createOrder(data: Record<string, unknown>, actions: any) {
if (debug) console.log("Creating order for amount", this.state.amount);
return actions.order
.create({
purchase_units: [
{
amount: {
value: this.state.amount
}
}
]
})
.then((orderID: any) => {
this.setState({ orderID: orderID });
return orderID;
});
}
onApprove(data: any, actions: any) {
let app = this;
return actions.order.capture().then(function (details: any) {
app.setState({
onApproveMessage: `Transaction completed by ${details.payer.name.given_name}!`
});
});
}
onError(err: Record<string, unknown>) {
this.setState({
onErrorMessage: err.toString()
});
}
onClick() {
if (debug) console.log("When clicked, amount was", this.state.amount);
}
render() {
return (
<div style={{ minHeight: "300px" }}>
<table className="table" style={{ maxWidth: "400px" }}>
<tbody>
<tr>
<th>
<label htmlFor="amount">Order Amount: </label>
</th>
<td>
<select onChange={this.onChange} name="amount" id="amount">
<option value="2.00">.00</option>
<option value="4.00">.00</option>
<option value="6.00">.00</option>
</select>
</td>
</tr>
<tr>
<th>Order ID:</th>
<td>{this.state.orderID ? this.state.orderID : "unknown"}</td>
</tr>
<tr>
<th>On Approve Message: </th>
<td data-testid="message">{this.state.onApproveMessage}</td>
</tr>
<tr>
<th>On Error Message: </th>
<td data-testid="error">{this.state.onErrorMessage}</td>
</tr>
</tbody>
</table>
<PayPalScriptProvider options={{ "client-id": "test" }}>
<PayPalButtons
createOrder={this.createOrder}
onApprove={this.onApprove}
onError={this.onError}
onClick={this.onClick}
/>
</PayPalScriptProvider>
</div>
);
}
}