@rachelallyson/hero-hook-form 2.7.2 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.8.0] - 2026-01-22
6
+
7
+ ### Added
8
+
9
+ - **Field Array Memory Leak Fixes**: Comprehensive solution for Cypress Electron renderer memory issues
10
+ - `FormFieldHelpers.conditionalFieldArray()` - Memory-safe conditional field arrays that prevent register/unregister cycles
11
+ - `alwaysRegistered` prop for `FieldArrayField` - Keeps fields registered but conditionally renders UI
12
+ - `useLazyFieldRegistration` and `useLazyFieldArrayRegistration` hooks - Lazy registration for better initial memory usage
13
+ - `fieldArrayMemory` utilities - Memory cleanup helpers with garbage collection hints
14
+ - Cypress memory optimizations - `experimentalMemoryManagement` and reduced memory retention
15
+
16
+ - **Performance Monitoring**: Enhanced field array performance tracking
17
+ - Memory usage monitoring for field array operations
18
+ - Performance metrics collection for long-running tests
19
+ - Garbage collection suggestions for memory-intensive operations
20
+
21
+ ### Fixed
22
+
23
+ - **Critical Bug Fix: Input Name Attributes**: Fixed missing `name` prop forwarding in `InputField` component
24
+ - `CoercedInput` now properly passes `name={field.name}` to HeroUI Input components
25
+ - Fixes accessibility issues where form inputs had no name attribute in DOM
26
+ - Critical for form automation tools, accessibility compliance, and proper form submission
27
+ - Affects all input types (text, email, password, tel, number, etc.) in ZodForm
28
+
29
+ ### Internal
30
+
31
+ - **Architecture Cleanup: Simplified HeroUI Integration**: Removed redundant dual-build system and `/react` subpath
32
+ - Eliminated duplicate `react/fields/` components and `tsconfig.react.json`
33
+ - Streamlined to single `#ui` alias approach that works with both individual `@heroui/*` packages and `@heroui/react`
34
+ - `@heroui/react` re-exports all components, making separate configurations unnecessary
35
+ - Cleaner codebase with same functionality and flexibility for users
36
+
5
37
  ## [2.7.2] - 2026-01-21
6
38
 
7
39
  ### Fixed
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import React$1, { ComponentProps } from 'react';
2
2
  import { Button } from '@heroui/react';
3
3
  import * as react_hook_form from 'react-hook-form';
4
- import { FieldValues, Path, RegisterOptions, ArrayPath, Control, UseFormReturn, FieldErrors, UseFormProps, SubmitHandler, DefaultValues, UseFormSetError, FieldArrayWithId } from 'react-hook-form';
4
+ import { FieldValues, Path, RegisterOptions, ArrayPath, Control, UseFormReturn, FieldErrors, UseFormProps, SubmitHandler, DefaultValues, UseFormSetError, FieldPath, FieldArrayWithId } from 'react-hook-form';
5
5
  export { UseFormReturn, useFormContext } from 'react-hook-form';
6
6
  import * as zod from 'zod';
7
7
  import { z, ZodSchema } from 'zod';
@@ -197,6 +197,8 @@ interface FieldArrayConfig<TFieldValues extends FieldValues> extends Omit<BaseFo
197
197
  };
198
198
  /** Function to create default item when adding new array item */
199
199
  defaultItem?: () => any;
200
+ /** Whether this field array should always be registered (for conditional rendering) */
201
+ alwaysRegistered?: boolean;
200
202
  /** Custom render function for array items */
201
203
  renderItem?: (props: {
202
204
  /** Item index (0-based) */
@@ -2597,6 +2599,41 @@ declare const FormFieldHelpers: {
2597
2599
  * ```
2598
2600
  */
2599
2601
  conditional: <T extends FieldValues = FieldValues>(name: Path<T>, condition: (formData: Partial<T>) => boolean, field: ZodFormFieldConfig<T>) => ZodFormFieldConfig<T>;
2602
+ /**
2603
+ * Create a conditional field array that avoids memory leaks in Cypress tests.
2604
+ *
2605
+ * This helper creates a field array that is always registered but conditionally
2606
+ * rendered, preventing the register/unregister cycles that cause memory
2607
+ * accumulation in Cypress Electron renderer.
2608
+ *
2609
+ * @param name - The field array name
2610
+ * @param condition - Function that determines if the field array should be visible
2611
+ * @param label - Display label for the field array
2612
+ * @param fields - Field configurations for array items
2613
+ * @param options - Additional field array options
2614
+ *
2615
+ * @example
2616
+ * ```tsx
2617
+ * // Memory-safe conditional field array for multiple choice options
2618
+ * FormFieldHelpers.conditionalFieldArray(
2619
+ * "choices",
2620
+ * (data) => data.questionType === 'MULTIPLE_CHOICE',
2621
+ * "Answer Choices",
2622
+ * [
2623
+ * FormFieldHelpers.input("text", "Choice Text"),
2624
+ * FormFieldHelpers.checkbox("isCorrect", "Correct Answer"),
2625
+ * ]
2626
+ * )
2627
+ * ```
2628
+ */
2629
+ conditionalFieldArray: <T extends FieldValues = FieldValues>(name: ArrayPath<T>, condition: (formData: Partial<T>) => boolean, label: string, fields: ZodFormFieldConfig<T>[], options?: {
2630
+ min?: number;
2631
+ max?: number;
2632
+ addButtonText?: string;
2633
+ removeButtonText?: string;
2634
+ enableReordering?: boolean;
2635
+ defaultItem?: () => any;
2636
+ }) => ZodFormFieldConfig<T>;
2600
2637
  /**
2601
2638
  * Create a content field for headers, questions, or custom content between fields
2602
2639
  *
@@ -3639,6 +3676,33 @@ declare function useTypeInferredForm<T extends FieldValues>(formConfig: {
3639
3676
  fields: ZodFormFieldConfig<T>[];
3640
3677
  }, options?: UseInferredFormOptions<T>): UseFormReturn<T>;
3641
3678
 
3679
+ /**
3680
+ * Hook for lazy field registration to reduce initial memory usage.
3681
+ *
3682
+ * This hook registers fields only when they become active (e.g., when a condition is met),
3683
+ * preventing the memory overhead of always-registered fields while avoiding
3684
+ * register/unregister cycles that cause memory leaks in Cypress.
3685
+ *
3686
+ * @param fieldName - The field name to potentially register
3687
+ * @param shouldRegister - Function that determines if the field should be registered
3688
+ * @param defaultValue - Default value for the field when registered
3689
+ * @param rules - Validation rules for the field
3690
+ */
3691
+ declare function useLazyFieldRegistration<TFieldValues extends FieldValues>(fieldName: FieldPath<TFieldValues>, shouldRegister: () => boolean, defaultValue?: any, rules?: any): {
3692
+ currentValue: undefined;
3693
+ isRegistered: boolean;
3694
+ };
3695
+ /**
3696
+ * Hook for lazy field array registration.
3697
+ *
3698
+ * Similar to useLazyFieldRegistration but specifically for field arrays,
3699
+ * which have more complex registration requirements.
3700
+ */
3701
+ declare function useLazyFieldArrayRegistration<TFieldValues extends FieldValues>(arrayName: FieldPath<TFieldValues>, shouldRegister: () => boolean, defaultValue?: any[]): {
3702
+ currentValue: any;
3703
+ isRegistered: boolean;
3704
+ };
3705
+
3642
3706
  /**
3643
3707
  * Props for the ConditionalField component.
3644
3708
  *
@@ -3718,6 +3782,8 @@ declare function ContentField<TFieldValues extends FieldValues>({ config, form,
3718
3782
  interface FieldArrayFieldProps<TFieldValues extends FieldValues> {
3719
3783
  config: FieldArrayConfig<TFieldValues>;
3720
3784
  className?: string;
3785
+ /** Whether this field array should always be registered (for conditional rendering) */
3786
+ alwaysRegistered?: boolean;
3721
3787
  }
3722
3788
  /**
3723
3789
  * Field array component for dynamic repeating field groups.
@@ -3834,7 +3900,7 @@ interface FieldArrayFieldProps<TFieldValues extends FieldValues> {
3834
3900
  * @see {@link createFieldArrayCustomConfig} for advanced custom rendering
3835
3901
  * @category Fields
3836
3902
  */
3837
- declare function FieldArrayField<TFieldValues extends FieldValues>({ className, config, }: FieldArrayFieldProps<TFieldValues>): React$1.JSX.Element | null;
3903
+ declare function FieldArrayField<TFieldValues extends FieldValues>({ alwaysRegistered, className, config, }: FieldArrayFieldProps<TFieldValues>): React$1.JSX.Element | null;
3838
3904
 
3839
3905
  /**
3840
3906
  * Props for the DynamicSectionField component.
@@ -4126,6 +4192,40 @@ interface CreateFieldArrayCustomConfigOptions<TFieldValues extends FieldValues>
4126
4192
  */
4127
4193
  declare function createFieldArrayCustomConfig<TFieldValues extends FieldValues>(options: CreateFieldArrayCustomConfigOptions<TFieldValues>): CustomFieldConfig<TFieldValues>;
4128
4194
 
4195
+ /**
4196
+ * Memory management utilities for field arrays to prevent memory leaks
4197
+ * in Cypress tests and long-running applications.
4198
+ */
4199
+ /**
4200
+ * Hook to clean up field array memory when component unmounts.
4201
+ * Helps prevent memory accumulation in Cypress Electron renderer.
4202
+ */
4203
+ declare function useFieldArrayMemoryCleanup<TFieldValues extends FieldValues>(arrayName: FieldPath<TFieldValues>): {
4204
+ cleanup: () => void;
4205
+ };
4206
+ /**
4207
+ * Utility to force garbage collection hints for Cypress tests.
4208
+ * Only effective when experimentalMemoryManagement is enabled.
4209
+ */
4210
+ declare function suggestGarbageCollection(): void;
4211
+ /**
4212
+ * Memory-safe field array operations that include cleanup hints.
4213
+ */
4214
+ declare const memorySafeFieldArray: {
4215
+ /**
4216
+ * Add items to a field array with memory management.
4217
+ */
4218
+ addItems: <TFieldValues extends FieldValues>(append: (value: any) => void, items: any[], onProgress?: (addedCount: number) => void) => void;
4219
+ /**
4220
+ * Clear entire field array with memory cleanup.
4221
+ */
4222
+ clearArray: <TFieldValues extends FieldValues>(setValue: (name: FieldPath<TFieldValues>, value: any) => void, arrayName: FieldPath<TFieldValues>) => void;
4223
+ /**
4224
+ * Remove items from a field array with memory cleanup.
4225
+ */
4226
+ removeItems: <TFieldValues extends FieldValues>(remove: (index: number) => void, indices: number[]) => void;
4227
+ };
4228
+
4129
4229
  /**
4130
4230
  * Common validation patterns for forms
4131
4231
  */
@@ -4209,4 +4309,4 @@ declare const validationUtils: {
4209
4309
  }>;
4210
4310
  };
4211
4311
 
4212
- export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type BaseFormFieldConfig, BasicFormBuilder, type BooleanFieldConfig, type ButtonDefaults, type CheckboxDefaults, CheckboxField, type CheckboxFieldProps, CheckboxGroupField, type CheckboxGroupFieldConfig, type CheckboxGroupFieldProps, type CommonFieldDefaults, CommonFields, ConditionalField, type ConditionalFieldConfig, type ConditionalFieldProps, type ConditionalValidation, ConfigurableForm, ContentField, type ContentFieldConfig, type CreateFieldArrayCustomConfigOptions, type CustomFieldConfig, DateField, type DateFieldConfig, type DateFieldProps, type DateInputDefaults, type DynamicSectionConfig, DynamicSectionField, type DynamicSectionFieldProps, type EnhancedFormState, FieldArrayBuilder, type FieldArrayConfig, FieldArrayField, type FieldArrayFieldProps, FieldArrayItemBuilder, type FieldBaseProps, type FieldCreationParams, type FieldGroup, FileField, type FileFieldConfig, type FileFieldProps, FontPickerField, type FontPickerFieldConfig, type FontPickerFieldProps, type FormConfig, FormField, type FormFieldConfig, FormFieldHelpers, type FormFieldType, type FormProps, FormProvider, FormStatus, type FormStatusProps, type FormStep, type FormSubmissionState, type FormTestUtils, FormToast, type FormToastProps, type FormValidationError, type HeroHookFormDefaultsConfig, HeroHookFormProvider, type HeroHookFormProviderProps, type InputDefaults, InputField, type InputFieldProps, type RadioFieldConfig, type RadioGroupDefaults, RadioGroupField, type RadioGroupFieldProps, type SelectDefaults, SelectField, type SelectFieldProps, ServerActionForm, type ServerFieldError, type ServerFormError, SimpleForm, type SimpleFormProps, type SliderDefaults, SliderField, type SliderFieldConfig, type SliderFieldProps, type StringArrayFieldConfig, type StringFieldConfig, SubmitButton, type SubmitButtonProps, type SwitchDefaults, SwitchField, type SwitchFieldProps, type TextareaDefaults, TextareaField, type TextareaFieldProps, TypeInferredBuilder, type UseDebouncedValidationOptions, type UseEnhancedFormStateOptions, type UseInferredFormOptions, type ValidationUtils, type WithControl, type WizardFormConfig, ZodForm, type ZodFormConfig, type ZodFormFieldConfig, applyServerErrors, asyncValidation, commonValidations, createAdvancedBuilder, createBasicFormBuilder, createDateSchema, createEmailSchema, createField, createFieldArrayBuilder, createFieldArrayCustomConfig, createFieldArrayItemBuilder, createFileSchema, createFormTestUtils, createFutureDateSchema, createMaxLengthSchema, createMinLengthSchema, createMockFormData, createMockFormErrors, createNestedPathBuilder, createNumberRangeSchema, createOptimizedFieldHandler, createPasswordSchema, createPastDateSchema, createPhoneSchema, createRequiredCheckboxSchema, createRequiredSchema, createTypeInferredBuilder, createUrlSchema, createZodFormConfig, crossFieldValidation, debounce, deepEqual, defineInferredForm, errorMessages, field, getFieldError, getFormErrors, hasFieldError, hasFormErrors, pathToString, serverValidation, shallowEqual, simulateFieldInput, simulateFormSubmission, syncArrays, throttle, useDebouncedFieldValidation, useDebouncedValidation, useEnhancedFormState, useFormHelper, useHeroForm, useHeroHookFormDefaults, useInferredForm, useMemoizedCallback, useMemoizedFieldProps, usePerformanceMonitor, useTypeInferredForm, useZodForm, validationPatterns, validationUtils, waitForFormState };
4312
+ export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type BaseFormFieldConfig, BasicFormBuilder, type BooleanFieldConfig, type ButtonDefaults, type CheckboxDefaults, CheckboxField, type CheckboxFieldProps, CheckboxGroupField, type CheckboxGroupFieldConfig, type CheckboxGroupFieldProps, type CommonFieldDefaults, CommonFields, ConditionalField, type ConditionalFieldConfig, type ConditionalFieldProps, type ConditionalValidation, ConfigurableForm, ContentField, type ContentFieldConfig, type CreateFieldArrayCustomConfigOptions, type CustomFieldConfig, DateField, type DateFieldConfig, type DateFieldProps, type DateInputDefaults, type DynamicSectionConfig, DynamicSectionField, type DynamicSectionFieldProps, type EnhancedFormState, FieldArrayBuilder, type FieldArrayConfig, FieldArrayField, type FieldArrayFieldProps, FieldArrayItemBuilder, type FieldBaseProps, type FieldCreationParams, type FieldGroup, FileField, type FileFieldConfig, type FileFieldProps, FontPickerField, type FontPickerFieldConfig, type FontPickerFieldProps, type FormConfig, FormField, type FormFieldConfig, FormFieldHelpers, type FormFieldType, type FormProps, FormProvider, FormStatus, type FormStatusProps, type FormStep, type FormSubmissionState, type FormTestUtils, FormToast, type FormToastProps, type FormValidationError, type HeroHookFormDefaultsConfig, HeroHookFormProvider, type HeroHookFormProviderProps, type InputDefaults, InputField, type InputFieldProps, type RadioFieldConfig, type RadioGroupDefaults, RadioGroupField, type RadioGroupFieldProps, type SelectDefaults, SelectField, type SelectFieldProps, ServerActionForm, type ServerFieldError, type ServerFormError, SimpleForm, type SimpleFormProps, type SliderDefaults, SliderField, type SliderFieldConfig, type SliderFieldProps, type StringArrayFieldConfig, type StringFieldConfig, SubmitButton, type SubmitButtonProps, type SwitchDefaults, SwitchField, type SwitchFieldProps, type TextareaDefaults, TextareaField, type TextareaFieldProps, TypeInferredBuilder, type UseDebouncedValidationOptions, type UseEnhancedFormStateOptions, type UseInferredFormOptions, type ValidationUtils, type WithControl, type WizardFormConfig, ZodForm, type ZodFormConfig, type ZodFormFieldConfig, applyServerErrors, asyncValidation, commonValidations, createAdvancedBuilder, createBasicFormBuilder, createDateSchema, createEmailSchema, createField, createFieldArrayBuilder, createFieldArrayCustomConfig, createFieldArrayItemBuilder, createFileSchema, createFormTestUtils, createFutureDateSchema, createMaxLengthSchema, createMinLengthSchema, createMockFormData, createMockFormErrors, createNestedPathBuilder, createNumberRangeSchema, createOptimizedFieldHandler, createPasswordSchema, createPastDateSchema, createPhoneSchema, createRequiredCheckboxSchema, createRequiredSchema, createTypeInferredBuilder, createUrlSchema, createZodFormConfig, crossFieldValidation, debounce, deepEqual, defineInferredForm, errorMessages, field, getFieldError, getFormErrors, hasFieldError, hasFormErrors, memorySafeFieldArray, pathToString, serverValidation, shallowEqual, simulateFieldInput, simulateFormSubmission, suggestGarbageCollection, syncArrays, throttle, useDebouncedFieldValidation, useDebouncedValidation, useEnhancedFormState, useFieldArrayMemoryCleanup, useFormHelper, useHeroForm, useHeroHookFormDefaults, useInferredForm, useLazyFieldArrayRegistration, useLazyFieldRegistration, useMemoizedCallback, useMemoizedFieldProps, usePerformanceMonitor, useTypeInferredForm, useZodForm, validationPatterns, validationUtils, waitForFormState };
package/dist/index.js CHANGED
@@ -530,6 +530,7 @@ import React9 from "react";
530
530
  import { useFieldArray, useFormContext as useFormContext3 } from "react-hook-form";
531
531
  import { Button as Button2 } from "@heroui/react";
532
532
  function FieldArrayField({
533
+ alwaysRegistered = false,
533
534
  className,
534
535
  config
535
536
  }) {
@@ -557,6 +558,9 @@ function FieldArrayField({
557
558
  });
558
559
  const canAdd = fields.length < max;
559
560
  const canRemove = fields.length > min;
561
+ if (alwaysRegistered && fields.length === 0) {
562
+ return null;
563
+ }
560
564
  const getFieldPath = (fieldName, itemIndex) => {
561
565
  return `${String(name)}.${itemIndex}.${fieldName}`;
562
566
  };
@@ -1614,7 +1618,8 @@ function FormFieldComponent({
1614
1618
  FieldArrayField,
1615
1619
  {
1616
1620
  config: fieldArrayConfig,
1617
- className: fieldArrayConfig.className
1621
+ className: fieldArrayConfig.className,
1622
+ alwaysRegistered: "alwaysRegistered" in fieldConfig ? fieldConfig.alwaysRegistered : false
1618
1623
  }
1619
1624
  );
1620
1625
  }
@@ -3256,6 +3261,57 @@ var FormFieldHelpers = {
3256
3261
  };
3257
3262
  return config;
3258
3263
  },
3264
+ /**
3265
+ * Create a conditional field array that avoids memory leaks in Cypress tests.
3266
+ *
3267
+ * This helper creates a field array that is always registered but conditionally
3268
+ * rendered, preventing the register/unregister cycles that cause memory
3269
+ * accumulation in Cypress Electron renderer.
3270
+ *
3271
+ * @param name - The field array name
3272
+ * @param condition - Function that determines if the field array should be visible
3273
+ * @param label - Display label for the field array
3274
+ * @param fields - Field configurations for array items
3275
+ * @param options - Additional field array options
3276
+ *
3277
+ * @example
3278
+ * ```tsx
3279
+ * // Memory-safe conditional field array for multiple choice options
3280
+ * FormFieldHelpers.conditionalFieldArray(
3281
+ * "choices",
3282
+ * (data) => data.questionType === 'MULTIPLE_CHOICE',
3283
+ * "Answer Choices",
3284
+ * [
3285
+ * FormFieldHelpers.input("text", "Choice Text"),
3286
+ * FormFieldHelpers.checkbox("isCorrect", "Correct Answer"),
3287
+ * ]
3288
+ * )
3289
+ * ```
3290
+ */
3291
+ conditionalFieldArray: (name, condition, label, fields, options) => {
3292
+ const fieldArrayConfig = {
3293
+ addButtonText: options?.addButtonText ?? "Add Item",
3294
+ alwaysRegistered: true,
3295
+ defaultItem: options?.defaultItem,
3296
+ enableReordering: options?.enableReordering ?? false,
3297
+ fields,
3298
+ label,
3299
+ max: options?.max ?? 10,
3300
+ // This prevents register/unregister cycles
3301
+ min: options?.min ?? 0,
3302
+ name,
3303
+ removeButtonText: options?.removeButtonText ?? "Remove",
3304
+ type: "fieldArray"
3305
+ };
3306
+ const config = {
3307
+ condition,
3308
+ field: fieldArrayConfig,
3309
+ name,
3310
+ // ArrayPath extends Path, so this is safe
3311
+ type: "conditional"
3312
+ };
3313
+ return config;
3314
+ },
3259
3315
  /**
3260
3316
  * Create a content field for headers, questions, or custom content between fields
3261
3317
  *
@@ -4475,8 +4531,74 @@ function useTypeInferredForm(formConfig, options = {}) {
4475
4531
  return useInferredForm(formConfig.schema, formConfig.fields, options);
4476
4532
  }
4477
4533
 
4534
+ // src/hooks/useLazyFieldRegistration.ts
4535
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
4536
+ import { useFormContext as useFormContext6 } from "react-hook-form";
4537
+ function useLazyFieldRegistration(fieldName, shouldRegister, defaultValue, rules) {
4538
+ const { register, setValue, unregister, watch } = useFormContext6();
4539
+ const [isRegistered, setIsRegistered] = useState4(false);
4540
+ const hasCheckedRegistration = useRef2(false);
4541
+ const conditionMet = shouldRegister();
4542
+ useEffect3(() => {
4543
+ if (conditionMet && !isRegistered) {
4544
+ register(fieldName, rules);
4545
+ if (defaultValue !== void 0) {
4546
+ setValue(fieldName, defaultValue);
4547
+ }
4548
+ setIsRegistered(true);
4549
+ hasCheckedRegistration.current = true;
4550
+ } else if (!conditionMet && isRegistered && hasCheckedRegistration.current) {
4551
+ unregister(fieldName);
4552
+ setIsRegistered(false);
4553
+ }
4554
+ }, [
4555
+ conditionMet,
4556
+ fieldName,
4557
+ register,
4558
+ unregister,
4559
+ setValue,
4560
+ rules,
4561
+ defaultValue,
4562
+ isRegistered
4563
+ ]);
4564
+ return {
4565
+ currentValue: isRegistered ? watch(fieldName) : void 0,
4566
+ isRegistered
4567
+ };
4568
+ }
4569
+ function useLazyFieldArrayRegistration(arrayName, shouldRegister, defaultValue = []) {
4570
+ const { setValue, watch } = useFormContext6();
4571
+ const [isRegistered, setIsRegistered] = useState4(false);
4572
+ const hasCheckedRegistration = useRef2(false);
4573
+ const conditionMet = shouldRegister();
4574
+ const currentValue = watch(arrayName);
4575
+ useEffect3(() => {
4576
+ if (conditionMet && !isRegistered) {
4577
+ if (!currentValue || !Array.isArray(currentValue)) {
4578
+ setValue(arrayName, defaultValue);
4579
+ }
4580
+ setIsRegistered(true);
4581
+ hasCheckedRegistration.current = true;
4582
+ } else if (!conditionMet && isRegistered && hasCheckedRegistration.current) {
4583
+ setValue(arrayName, []);
4584
+ setIsRegistered(false);
4585
+ }
4586
+ }, [
4587
+ conditionMet,
4588
+ arrayName,
4589
+ setValue,
4590
+ defaultValue,
4591
+ isRegistered,
4592
+ currentValue
4593
+ ]);
4594
+ return {
4595
+ currentValue: isRegistered ? currentValue : [],
4596
+ isRegistered
4597
+ };
4598
+ }
4599
+
4478
4600
  // src/utils/performance.ts
4479
- import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef2 } from "react";
4601
+ import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef3 } from "react";
4480
4602
  function debounce(func, delay) {
4481
4603
  let timeoutId;
4482
4604
  return (...args) => {
@@ -4495,7 +4617,7 @@ function throttle(func, limit) {
4495
4617
  };
4496
4618
  }
4497
4619
  function useMemoizedCallback(callback, deps) {
4498
- const callbackRef = useRef2(callback);
4620
+ const callbackRef = useRef3(callback);
4499
4621
  callbackRef.current = callback;
4500
4622
  return useCallback3(
4501
4623
  ((...args) => callbackRef.current(...args)),
@@ -4541,8 +4663,8 @@ function deepEqual(prevProps, nextProps) {
4541
4663
  return true;
4542
4664
  }
4543
4665
  function usePerformanceMonitor(componentName, enabled = process.env.NODE_ENV === "development") {
4544
- const renderCountRef = useRef2(0);
4545
- const lastRenderTimeRef = useRef2(Date.now());
4666
+ const renderCountRef = useRef3(0);
4667
+ const lastRenderTimeRef = useRef3(Date.now());
4546
4668
  if (enabled) {
4547
4669
  renderCountRef.current += 1;
4548
4670
  const now = Date.now();
@@ -4702,6 +4824,76 @@ function createFieldArrayCustomConfig(options) {
4702
4824
  };
4703
4825
  }
4704
4826
 
4827
+ // src/utils/fieldArrayMemory.ts
4828
+ import { useFormContext as useFormContext7 } from "react-hook-form";
4829
+ function useFieldArrayMemoryCleanup(arrayName) {
4830
+ const { reset, unregister } = useFormContext7();
4831
+ const cleanup = () => {
4832
+ try {
4833
+ reset((formValues) => ({
4834
+ ...formValues,
4835
+ [arrayName]: []
4836
+ }));
4837
+ setTimeout(() => {
4838
+ try {
4839
+ unregister(arrayName);
4840
+ } catch (error) {
4841
+ console.debug("Field array cleanup unregister failed:", error);
4842
+ }
4843
+ }, 0);
4844
+ } catch (error) {
4845
+ console.debug("Field array cleanup failed:", error);
4846
+ }
4847
+ };
4848
+ return { cleanup };
4849
+ }
4850
+ function suggestGarbageCollection() {
4851
+ if (typeof window !== "undefined" && "gc" in window) {
4852
+ try {
4853
+ window.gc();
4854
+ } catch (error) {
4855
+ }
4856
+ }
4857
+ }
4858
+ var memorySafeFieldArray = {
4859
+ /**
4860
+ * Add items to a field array with memory management.
4861
+ */
4862
+ addItems: (append, items, onProgress) => {
4863
+ let addedCount = 0;
4864
+ const batchSize = 10;
4865
+ for (let i = 0; i < items.length; i += batchSize) {
4866
+ const batch = items.slice(i, i + batchSize);
4867
+ batch.forEach((item) => {
4868
+ append(item);
4869
+ addedCount++;
4870
+ });
4871
+ if (addedCount % batchSize === 0) {
4872
+ suggestGarbageCollection();
4873
+ onProgress?.(addedCount);
4874
+ }
4875
+ }
4876
+ suggestGarbageCollection();
4877
+ },
4878
+ /**
4879
+ * Clear entire field array with memory cleanup.
4880
+ */
4881
+ clearArray: (setValue, arrayName) => {
4882
+ setValue(arrayName, []);
4883
+ suggestGarbageCollection();
4884
+ },
4885
+ /**
4886
+ * Remove items from a field array with memory cleanup.
4887
+ */
4888
+ removeItems: (remove, indices) => {
4889
+ const sortedIndices = [...indices].sort((a, b) => b - a);
4890
+ sortedIndices.forEach((index) => {
4891
+ remove(index);
4892
+ });
4893
+ suggestGarbageCollection();
4894
+ }
4895
+ };
4896
+
4705
4897
  // src/builders/validation-helpers.ts
4706
4898
  import { z as z4 } from "zod";
4707
4899
  var validationPatterns = {
@@ -4922,21 +5114,26 @@ export {
4922
5114
  getFormErrors,
4923
5115
  hasFieldError,
4924
5116
  hasFormErrors,
5117
+ memorySafeFieldArray,
4925
5118
  pathToString,
4926
5119
  serverValidation,
4927
5120
  shallowEqual,
4928
5121
  simulateFieldInput,
4929
5122
  simulateFormSubmission,
5123
+ suggestGarbageCollection,
4930
5124
  syncArrays,
4931
5125
  throttle,
4932
5126
  useDebouncedFieldValidation,
4933
5127
  useDebouncedValidation,
4934
5128
  useEnhancedFormState,
5129
+ useFieldArrayMemoryCleanup,
4935
5130
  useFormContext5 as useFormContext,
4936
5131
  useFormHelper,
4937
5132
  useHeroForm,
4938
5133
  useHeroHookFormDefaults,
4939
5134
  useInferredForm,
5135
+ useLazyFieldArrayRegistration,
5136
+ useLazyFieldRegistration,
4940
5137
  useMemoizedCallback,
4941
5138
  useMemoizedFieldProps,
4942
5139
  usePerformanceMonitor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rachelallyson/hero-hook-form",
3
- "version": "2.7.2",
3
+ "version": "2.8.0",
4
4
  "description": "Typed form helpers that combine React Hook Form and HeroUI components.",
5
5
  "author": "Rachel Higley",
6
6
  "homepage": "https://rachelallyson.github.io/hero-hook-form/",