Skip to content

feat: persist user input values when selected payment method is changed - #1692

Open
Shivam25092001 wants to merge 5 commits into
mainfrom
feat-persist-user-inputs
Open

feat: persist user input values when selected payment method is changed#1692
Shivam25092001 wants to merge 5 commits into
mainfrom
feat-persist-user-inputs

Conversation

@Shivam25092001

@Shivam25092001 Shivam25092001 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

Persists user-entered dynamic field values when the selected payment method type changes, so a shopper who types into shared fields (name, email, phone, address, etc.) doesn't lose that input after switching payment methods.

Why this happens:

In PaymentElement.res the payment-method subtree is rendered under <ErrorBoundary key={selectedOption} ...>. Switching the payment method type changes the key and remounts the whole subtree (DynamicFields → ReactFinalForm.Form), destroying all form state. Previously only Country survived, because it round-trips through the persistent userCountry atom.

Changes:

  • New Recoil atom userDynamicFieldsValues : Dict.t<string> in RecoilAtoms.res, keyed by confirmRequestWritePath. It lives above the remount boundary, so its contents survive the payment-method switch.
  • Persist on change — in FormBody's onFormChange, the typed values of persistable fields are written into the atom every time the form updates.
  • Restore on remount — a new initialValuesWithUserInputOverride memo seeds the form's initialValues from the cached atom (layered on top of the existing billing-details override), and the ReactFinalForm.Form now consumes this override.
  • New persistableFields set — the fields whose values we persist. Unlike missingRequiredFieldsFiltered (which dedups Email/CardHolderName down to the single visible input), this keeps both name paths (first_name + last_name) and every email path, since the combined name/email inputs write all of them into RFF. It excludes self-managed fields (Country / PhoneCountryCode), which own their value outside RFF and are seeded through their own atom/local-state path.
  • Refactor — extracted the shared "does this field render a visible input?" logic out of the missingRequiredFieldsFiltered filter into a single rendersVisibleInput helper, reused by both missingRequiredFieldsFiltered and persistableFields.

No change to the '/confirm' payload shape — seeding stays restricted to the current payment method's rendered fields.

How did you test it?

Manually verified in the SDK: entered values into shared dynamic fields (name / email / phone / address), switched between payment method types, and confirmed the previously typed values are restored on the re-rendered form instead of being wiped.

Screen.Recording.2026-08-06.at.6.18.32.PM.mov

Checklist

  • I ran npm run re:build
  • I reviewed submitted code
  • I added unit tests for my changes where possible

@semanticdiff-com

Copy link
Copy Markdown

Review changes with  SemanticDiff

@XyneSpaces

Copy link
Copy Markdown

[should-fix] Recompute initialValuesWithUserInputOverride when the persisted dynamic-field cache or persistableFields changes. With the current dependency list, updates to userDynamicFieldsValues can re-render this component but keep the memoized initial values stale, so a payment-method switch can still remount the form without the latest typed values.

  }, [initialValuesWithBillingDataOverride, cachedUserDynamicFieldsValues, persistableFields])

Comment thread src/Utilities/RecoilAtoms.res Outdated
Comment thread src/Components/DynamicFields.res Outdated
Comment thread src/Components/DynamicFields.res Outdated
aritro2002
aritro2002 previously approved these changes Aug 7, 2026
Comment thread src/Components/DynamicFields.res Outdated
// local state), so they're seeded through that path, not the persistence cache.
let isPersistableRenderType = field =>
switch field.fieldRenderType {
| Country | PhoneCountryCode => false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could you please explain why PhoneCountryCode and Country are marked as false

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.

They are being maintained already by userCountry atom.

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.

Also, PhoneCountryCode is excluded because its dropdown is controlled by component-local state (valueDropDown)

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.

Removed isPersistableRenderType entirely to simplify the logic.

Description:

  • CountryDropdownField derives the rendered country from field.input.value (storedIso), falling back to the userCountry atom only when RFF is empty, and pushes the effective value back into the atom via an effect. Since RFF is the display source of truth, replaying the cached user value into initialValues before the field re-registers is enough to restore the pick — the atom path is just a fallback, not a competing owner.

  • PhoneCountryCodeDropdownField has the paired fix: The component rebuilds that UI value from field.input.value (storedCode) so a cached dial code survives the remount.

With both fields reading from RFF, the generic persistence path works for them the same way it works for every other dynamic field.

@sakksham7

Copy link
Copy Markdown
Contributor

Can you also please add a use case of Update Intent flow with these new changes in the PR description?

@Shivam25092001

Copy link
Copy Markdown
Contributor Author

Can you also please add a use case of Update Intent flow with these new changes in the PR description?

Screen.Recording.2026-08-12.at.3.17.59.PM.mov

@sakksham7

sakksham7
sakksham7 previously approved these changes Aug 12, 2026
@sakksham7

Copy link
Copy Markdown
Contributor

Can you also please add a use case of Update Intent flow with these new changes in the PR description?

Screen.Recording.2026-08-12.at.3.17.59.PM.mov
@sakksham7

CC: @ArushKapoorJuspay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persist address field values when switching payment methods [UX enhacement]

6 participants