|
| 1 | +--- |
| 2 | +"@bigcommerce/catalyst-core": patch |
| 3 | +--- |
| 4 | + |
| 5 | +Refactor DynamicForm actions to decouple fields and passwordComplexity from state, passing them as separate arguments instead. This reduces state payload size by removing fields from state objects and stripping options from fields before passing them to actions (options are only needed for rendering, not processing). All form actions now accept a `DynamicFormActionArgs` object as the first parameter containing fields and optional passwordComplexity, followed by the previous state and formData. |
| 6 | + |
| 7 | +## Migration steps |
| 8 | + |
| 9 | +### Step 1: Changes to DynamicForm component |
| 10 | + |
| 11 | +The `DynamicForm` component and related utilities have been updated to support the new action signature pattern: |
| 12 | + |
| 13 | +**`core/vibes/soul/form/dynamic-form/index.tsx`**: |
| 14 | +- Added `DynamicFormActionArgs<F>` interface that contains `fields` and optional `passwordComplexity` |
| 15 | +- Updated `DynamicFormAction<F>` type to accept `DynamicFormActionArgs<F>` as the first parameter |
| 16 | +- Removed `fields` and `passwordComplexity` from the `State` interface |
| 17 | +- Added automatic removal of `options` from fields before passing to actions (options are only needed for rendering) |
| 18 | +- Updated action binding to use `action.bind(null, { fields: fieldsWithoutOptions, passwordComplexity })` |
| 19 | + |
| 20 | +**`core/vibes/soul/form/dynamic-form/utils.ts`** (new file): |
| 21 | +- Added `removeOptionsFromFields()` utility function that strips the `options` property from field definitions before passing them to actions, reducing the state payload size |
| 22 | + |
| 23 | +```diff |
| 24 | ++ export interface DynamicFormActionArgs<F extends Field> { |
| 25 | ++ fields: Array<F | FieldGroup<F>>; |
| 26 | ++ passwordComplexity?: PasswordComplexitySettings | null; |
| 27 | ++ } |
| 28 | ++ |
| 29 | ++ type Action<F extends Field, S, P> = ( |
| 30 | ++ args: DynamicFormActionArgs<F>, |
| 31 | ++ state: Awaited<S>, |
| 32 | ++ payload: P, |
| 33 | ++ ) => S | Promise<S>; |
| 34 | ++ |
| 35 | + interface State { |
| 36 | + lastResult: SubmissionResult | null; |
| 37 | +- fields: Array<F | FieldGroup<F>>; |
| 38 | +- passwordComplexity?: PasswordComplexitySettings | null; |
| 39 | + } |
| 40 | +``` |
| 41 | + |
| 42 | +### Step 2: Update DynamicForm action signatures |
| 43 | + |
| 44 | +All form actions that use `DynamicForm` must be updated to accept `DynamicFormActionArgs<F>` as the first parameter instead of including fields in the state. |
| 45 | + |
| 46 | +Update your form action function signature: |
| 47 | + |
| 48 | +```diff |
| 49 | ++ import { DynamicFormActionArgs } from '@/vibes/soul/form/dynamic-form'; |
| 50 | + import { Field, FieldGroup, schema } from '@/vibes/soul/form/dynamic-form/schema'; |
| 51 | + |
| 52 | +- export async function myFormAction<F extends Field>( |
| 53 | +- prevState: { |
| 54 | +- lastResult: SubmissionResult | null; |
| 55 | +- fields: Array<F | FieldGroup<F>>; |
| 56 | +- passwordComplexity?: PasswordComplexitySettings | null; |
| 57 | +- }, |
| 58 | +- formData: FormData, |
| 59 | +- ) { |
| 60 | ++ export async function myFormAction<F extends Field>( |
| 61 | ++ { fields, passwordComplexity }: DynamicFormActionArgs<F>, |
| 62 | ++ _prevState: { |
| 63 | ++ lastResult: SubmissionResult | null; |
| 64 | ++ }, |
| 65 | ++ formData: FormData, |
| 66 | ++ ) { |
| 67 | +``` |
| 68 | + |
| 69 | +### Step 2: Remove fields and passwordComplexity from state interfaces |
| 70 | + |
| 71 | +Update state interfaces to remove fields and passwordComplexity properties: |
| 72 | + |
| 73 | +```diff |
| 74 | + interface State { |
| 75 | + lastResult: SubmissionResult | null; |
| 76 | +- fields: Array<Field | FieldGroup<Field>>; |
| 77 | +- passwordComplexity?: PasswordComplexitySettings | null; |
| 78 | + } |
| 79 | +``` |
| 80 | + |
| 81 | +### Step 3: Update action implementations |
| 82 | + |
| 83 | +Remove references to `prevState.fields` and `prevState.passwordComplexity` in action implementations: |
| 84 | + |
| 85 | +```diff |
| 86 | + const submission = parseWithZod(formData, { |
| 87 | +- schema: schema(prevState.fields, prevState.passwordComplexity), |
| 88 | ++ schema: schema(fields, passwordComplexity), |
| 89 | + }); |
| 90 | + |
| 91 | + if (submission.status !== 'success') { |
| 92 | + return { |
| 93 | + lastResult: submission.reply(), |
| 94 | +- fields: prevState.fields, |
| 95 | +- passwordComplexity: prevState.passwordComplexity, |
| 96 | + }; |
| 97 | + } |
| 98 | +``` |
| 99 | + |
| 100 | +### Step 4: Update action calls in components |
| 101 | + |
| 102 | +For actions used with `AddressListSection`, update the action signature to accept fields as the first parameter: |
| 103 | + |
| 104 | +```diff |
| 105 | +- export async function addressAction( |
| 106 | +- prevState: Awaited<State>, |
| 107 | +- formData: FormData, |
| 108 | +- ): Promise<State> { |
| 109 | ++ export async function addressAction( |
| 110 | ++ fields: Array<Field | FieldGroup<Field>>, |
| 111 | ++ prevState: Awaited<State>, |
| 112 | ++ formData: FormData, |
| 113 | ++ ): Promise<State> { |
| 114 | +``` |
| 115 | + |
| 116 | +### Step 5: Update DynamicForm usage |
| 117 | + |
| 118 | +No changes needed to `DynamicForm` component usage. The component automatically handles binding fields and passwordComplexity to actions. The `DynamicForm` component now: |
| 119 | +- Automatically removes options from fields before passing them to actions (reducing payload size) |
| 120 | +- Binds fields and passwordComplexity to the action using `action.bind()` |
| 121 | +- Maintains the same props interface, so existing usage continues to work |
| 122 | + |
| 123 | +### Affected files |
| 124 | + |
| 125 | +The following files were updated in this refactor: |
| 126 | +- `core/vibes/soul/form/dynamic-form/index.tsx` - Added `DynamicFormActionArgs` type and updated action binding |
| 127 | +- `core/vibes/soul/form/dynamic-form/utils.ts` - Added `removeOptionsFromFields` utility function |
| 128 | +- `core/app/[locale]/(default)/(auth)/register/_actions/register-customer.ts` |
| 129 | +- `core/app/[locale]/(default)/account/addresses/_actions/address-action.ts` |
| 130 | +- `core/app/[locale]/(default)/account/addresses/_actions/create-address.ts` |
| 131 | +- `core/app/[locale]/(default)/account/addresses/_actions/update-address.ts` |
| 132 | +- `core/app/[locale]/(default)/account/addresses/_actions/delete-address.ts` |
| 133 | +- `core/app/[locale]/(default)/gift-certificates/purchase/_actions/add-to-cart.tsx` |
| 134 | +- `core/app/[locale]/(default)/webpages/[id]/contact/_actions/submit-contact-form.ts` |
| 135 | +- `core/vibes/soul/sections/address-list-section/index.tsx` |
0 commit comments