feat: add useConditionalField hook and implement reactive form showcase examples
This commit is contained in:
@@ -1 +1,2 @@
|
||||
export * from '@mantine/hooks';
|
||||
export * from './useConditionalField';
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useEffect } from 'react';
|
||||
import type { UseFormSetValue, UseFormUnregister, UseFormClearErrors, FieldValues, Path } from 'react-hook-form';
|
||||
|
||||
export interface UseConditionalFieldOptions<TFieldValues extends FieldValues> {
|
||||
condition: boolean;
|
||||
name: Path<TFieldValues>;
|
||||
setValue: UseFormSetValue<TFieldValues>;
|
||||
unregister?: UseFormUnregister<TFieldValues>;
|
||||
clearErrors?: UseFormClearErrors<TFieldValues>;
|
||||
defaultValue?: any;
|
||||
mode?: 'unregister' | 'reset';
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically cleans up a conditionally rendered React Hook Form field
|
||||
* when its parent condition becomes false.
|
||||
*
|
||||
* @param options Configuration object for the conditional field behavior
|
||||
*/
|
||||
export function useConditionalField<TFieldValues extends FieldValues>(
|
||||
options: UseConditionalFieldOptions<TFieldValues>
|
||||
) {
|
||||
// Destructure with default values
|
||||
const {
|
||||
condition,
|
||||
name,
|
||||
setValue,
|
||||
unregister,
|
||||
clearErrors,
|
||||
defaultValue,
|
||||
mode = 'unregister'
|
||||
} = options;
|
||||
|
||||
const config = options;
|
||||
|
||||
useEffect(() => {
|
||||
// When the condition evaluates to false, we execute the cleanup logic
|
||||
if (!condition) {
|
||||
// 1. Reset the field value. We use a stable empty state (like '' instead of undefined)
|
||||
// to prevent uncontrolled component fallback in the React UI. We also force RHF to sync.
|
||||
const targetValue = config.defaultValue !== undefined ? config.defaultValue : ('' as any);
|
||||
config.setValue(config.name, targetValue, {
|
||||
shouldDirty: true,
|
||||
shouldTouch: true,
|
||||
shouldValidate: true
|
||||
});
|
||||
|
||||
// 2. Execute the appropriate side-effect strategy based on the active mode
|
||||
if (mode === 'unregister' && config.unregister) {
|
||||
// Unregister Mode: Completely unmounts the field from React Hook Form.
|
||||
// The value is removed from the payload and validation is entirely bypassed.
|
||||
config.unregister(config.name);
|
||||
} else if (mode === 'reset' && config.clearErrors) {
|
||||
// Reset Mode: Keeps the field active in the DOM (e.g. cascading or disabled dependencies).
|
||||
// Wipes the value and clears active validation errors so the user can interact
|
||||
// with a fresh state, but keeps the property in the payload.
|
||||
config.clearErrors(config.name);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
condition,
|
||||
name,
|
||||
setValue,
|
||||
unregister,
|
||||
clearErrors,
|
||||
defaultValue,
|
||||
mode
|
||||
]);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z, type ZodString, type ZodNumber, type ZodTypeAny } from 'zod';
|
||||
import type { ZodString, ZodNumber, ZodTypeAny } from 'zod';
|
||||
|
||||
// ─── UTILITIES ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user