Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@juspay-tech/react-hyper-js",
"version": "2.4.0",
"version": "2.5.0",
"main": "dist/bundle.js",
"files": [
"dist/",
Expand Down
44 changes: 22 additions & 22 deletions src/Index.resi
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,28 @@ let \"HyperElements": HyperElements.props<
RescriptCore.Promise.t<OrcaJs.switchInstance>,
RescriptCore.JSON.t,
> => React.element
let \"PaymentElement": PaymentElement.props<
string,
RescriptCore.JSON.t,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
unit => RescriptCore.Promise.t<unit>,
> => React.element
let \"UnifiedCheckout": PaymentElement.props<
string,
RescriptCore.JSON.t,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
option<option<RescriptCore.JSON.t> => unit>,
unit => RescriptCore.Promise.t<unit>,
> => React.element
let \"PaymentElement": React.component<{
"id": string,
"options": RescriptCore.JSON.t,
"onChange": option<option<RescriptCore.JSON.t> => unit>,
"onReady": option<option<RescriptCore.JSON.t> => unit>,
"onFocus": option<option<RescriptCore.JSON.t> => unit>,
"onBlur": option<option<RescriptCore.JSON.t> => unit>,
"onClick": option<option<RescriptCore.JSON.t> => unit>,
"onPaymentComplete": option<option<RescriptCore.JSON.t> => unit>,
"onPaymentButtonClick": unit => RescriptCore.Promise.t<unit>,
}>
let \"UnifiedCheckout": React.component<{
"id": string,
"options": RescriptCore.JSON.t,
"onChange": option<option<RescriptCore.JSON.t> => unit>,
"onReady": option<option<RescriptCore.JSON.t> => unit>,
"onFocus": option<option<RescriptCore.JSON.t> => unit>,
"onBlur": option<option<RescriptCore.JSON.t> => unit>,
"onClick": option<option<RescriptCore.JSON.t> => unit>,
"onPaymentComplete": option<option<RescriptCore.JSON.t> => unit>,
"onPaymentButtonClick": unit => RescriptCore.Promise.t<unit>,
}>
let \"CardElement": CardElement.props<
string,
RescriptCore.JSON.t,
Expand Down
35 changes: 18 additions & 17 deletions src/components/PaymentElement.bs.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 24 additions & 21 deletions src/components/PaymentElement.res
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
@react.component
let make = (
~id,
~options: JSON.t,
~onChange,
~onReady,
~onFocus,
~onBlur,
~onClick,
~onPaymentComplete,
~onPaymentButtonClick,
let make = React.forwardRef((
props: {
"id": string,
"options": JSON.t,
"onChange": option<option<JSON.t> => unit>,
"onReady": option<option<JSON.t> => unit>,
"onFocus": option<option<JSON.t> => unit>,
"onBlur": option<option<JSON.t> => unit>,
"onClick": option<option<JSON.t> => unit>,
"onPaymentComplete": option<option<JSON.t> => unit>,
"onPaymentButtonClick": unit => Promise.t<unit>,
},
ref_,
) => {
<PaymentElementsWrapper
id
options
onChange
onReady
onFocus
onBlur
onClick
id=props["id"]
options=props["options"]
onChange=props["onChange"]
onReady=props["onReady"]
onFocus=props["onFocus"]
onBlur=props["onBlur"]
onClick=props["onClick"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why these changes were needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change from @react.component let make = (~id, ...) to React.forwardRef((props, ref_) => ...) was required to support ref-forwarding.
Also, the peer dependency is "react": "^17.0.0 || ^18.0.0 || ^19.0.0". This is a library, and its consumers may be on React 17 or 18. Dropping forwardRef in favor of React 19's ref-as-prop would silently break for those consumers.

componentType="payment"
onPaymentComplete
onPaymentButtonClick
onPaymentComplete=props["onPaymentComplete"]
onPaymentButtonClick=props["onPaymentButtonClick"]
imperativeRef=ref_
/>
}
})
7 changes: 7 additions & 0 deletions src/components/PaymentElementsWrapper.bs.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions src/components/PaymentElementsWrapper.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
type paymentElementHandle = {confirmPayment: JSON.t => Promise.t<JSON.t>}

@react.component
let make = (
~id="payment-Element",
Expand All @@ -10,13 +12,22 @@ let make = (
~onClick,
~onPaymentComplete,
~onPaymentButtonClick,
~imperativeRef: Nullable.t<React.ref<paymentElementHandle>>=Nullable.null,
) => {
let hyperSwitch = React.useContext(Context.switchContext)
let elementsState = React.useContext(Context.elementsContext)
let divRef = React.useRef(Nullable.null)

let paymentElement = elementsState.create(componentType, options)

React.useImperativeHandle1(
imperativeRef,
() => {
confirmPayment: hyperSwitch.confirmPayment,
},
[hyperSwitch],
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imperative handle is installed on the first commit, while switchContext is still defaultSwitchContext. Calling ref.current.confirmPayment() before the Hyper promise/element is ready therefore resolves {} and performs no request. Should we gate this on real element readiness (or reject with a stable sdk_not_ready error) instead of exposing the default no-op implementation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handle isn't frozen on mount — useImperativeHandle1 re-runs whenever hyperSwitch changes (the [hyperSwitch] dep array), so once the SDK context resolves the ref updates automatically.
That said, you're right that there's a window where calling ref.current.confirmPayment() too early would silently no-op using defaultSwitchContext. We could address this by checking ready event which is fired in almost every widget.
image

React.useEffect2(() => {
let paymentElement = elementsState.create(componentType, options)
paymentElement.mount(`#orca-elements-payment-element-${id}`)
Expand Down
Loading