forked from paypal/react-paypal-js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PayPalMarks.tsx
114 lines (103 loc) · 4.3 KB
/
PayPalMarks.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import React, { useEffect, useRef, useState, FC, ReactNode } from "react";
import { usePayPalScriptReducer } from "../hooks/scriptProviderHooks";
import { getPayPalWindowNamespace, generateErrorMessage } from "../utils";
import { DATA_NAMESPACE } from "../constants";
import type {
PayPalMarksComponentOptions,
PayPalMarksComponent,
} from "@paypal/paypal-js/types/components/marks";
export interface PayPalMarksComponentProps extends PayPalMarksComponentOptions {
/**
* Pass a css class to the div container.
*/
className?: string;
children?: ReactNode;
}
/**
The `<PayPalMarks />` component is used for conditionally rendering different payment options using radio buttons.
The [Display PayPal Buttons with other Payment Methods guide](https://developer.paypal.com/docs/business/checkout/add-capabilities/buyer-experience/#display-paypal-buttons-with-other-payment-methods) describes this style of integration in detail.
It relies on the `<PayPalScriptProvider />` parent component for managing state related to loading the JS SDK script.
```jsx
<PayPalMarks />
```
This component can also be configured to use a single funding source similar to the [standalone buttons](https://developer.paypal.com/docs/business/checkout/configure-payments/standalone-buttons/) approach.
A `FUNDING` object is exported by this library which has a key for every available funding source option.
```jsx
import { PayPalScriptProvider, PayPalMarks, FUNDING } from "@paypal/react-paypal-js";
<PayPalScriptProvider options={{ "client-id": "test", components: "buttons,marks" }}>
<PayPalMarks fundingSource={FUNDING.PAYPAL}/>
</PayPalScriptProvider>
```
*/
export const PayPalMarks: FC<PayPalMarksComponentProps> = ({
className = "",
children,
...markProps
}: PayPalMarksComponentProps) => {
const [{ isResolved, options }] = usePayPalScriptReducer();
const markContainerRef = useRef<HTMLDivElement>(null);
const [isEligible, setIsEligible] = useState(true);
const [, setErrorState] = useState(null);
/**
* Render PayPal Mark into the DOM
*/
const renderPayPalMark = (mark: PayPalMarksComponent) => {
const { current } = markContainerRef;
// only render the mark when eligible
if (!current || !mark.isEligible()) {
return setIsEligible(false);
}
// Remove any children before render it again
if (current.firstChild) {
current.removeChild(current.firstChild);
}
mark.render(current).catch((err) => {
// component failed to render, possibly because it was closed or destroyed.
if (current === null || current.children.length === 0) {
// paypal marks container is no longer in the DOM, we can safely ignore the error
return;
}
// paypal marks container is still in the DOM
setErrorState(() => {
throw new Error(
`Failed to render <PayPalMarks /> component. ${err}`
);
});
});
};
useEffect(() => {
// verify the sdk script has successfully loaded
if (isResolved === false) return;
const paypalWindowNamespace = getPayPalWindowNamespace(
options[DATA_NAMESPACE]
);
// verify dependency on window object
if (
paypalWindowNamespace === undefined ||
paypalWindowNamespace.Marks === undefined
) {
return setErrorState(() => {
throw new Error(
generateErrorMessage({
reactComponentName: PayPalMarks.displayName as string,
sdkComponentKey: "marks",
sdkRequestedComponents: options.components,
sdkDataNamespace: options[DATA_NAMESPACE],
})
);
});
}
renderPayPalMark(paypalWindowNamespace.Marks({ ...markProps }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isResolved, markProps.fundingSource]);
return (
<>
{isEligible ? (
<div ref={markContainerRef} className={className} />
) : (
children
)}
</>
);
};
PayPalMarks.displayName = "PayPalMarks";