auto-form icon indicating copy to clipboard operation
auto-form copied to clipboard

Improvement: Use `Controller` in `react` package to simplify and decouple form field implementation

Open adityacodepublic opened this issue 6 months ago • 6 comments

Problem

Many input fields across our UI packages are facing issues such as:

  • Default values not showing
  • Select components not registering values properly
  • undefined values being returned

These issues are consistently resolved when using useController from React Hook Form (RHF) instead of the register method.

While trying to resolve these issues, I ended up using useController in many fields. https://github.com/adityacodepublic/autoform/tree/fix-bugs

Improvement Suggestion

Move to using Controller in the react package, in the AutoFormField component, instead of relying on register. This change would:

  1. Fix form behavior issues: Components behave as expected without bugs.
  2. Decouple UI lib. from RHF: The UI package becomes form-library-agnostic, avoiding peer dependency on RHF.
  3. Simplify usage: Users don’t need to use useController manually.
  4. Extensible: Allows us to support other form libraries like TanStack Forms in the future with minimal changes.

Additional Context

  • We are already using useController instead of register in many ui libs.
  • Since Controller wraps only the FieldComponent inside AutoFormField, re-renders are scoped to just the FieldComponent. The outer AutoFormField or FieldWrapper does not re-render on controlled state changes.

  return (
    <FieldWrapper
      label={getLabel(field)}
      error={error}
      id={fullPath}
      field={field}
    >
      <Controller
        name={fullPath}
        disabled={field.fieldConfig?.inputProps?.disabled}
        render={({ field: formField }) => (
          <FieldComponent
            label={getLabel(field)}
            field={field}
            value={value}
            error={error}
            id={fullPath}
            key={fullPath}
            path={path}
            inputProps={{
              error: error,
              key: `${fullPath}-input`,
              ...field.fieldConfig?.inputProps,
              ...formField,
            }}
          />
        )}
      />
    </FieldWrapper>
  );
};

@vantezzen Let me know your thoughts — happy to help implement this if it looks good to you.

adityacodepublic avatar Jul 04 '25 13:07 adityacodepublic

libs like tanstack forms use controlled by default https://tanstack.com/form/latest/docs/philosophy#controlled-is-cool

shadcn also uses Controller for forms https://ui.shadcn.com/docs/components/form

adityacodepublic avatar Jul 04 '25 13:07 adityacodepublic

Sounds very interesting, would be good to solve all these issues with one fix! Feel free to move on with this

vantezzen avatar Jul 09 '25 07:07 vantezzen

I initially tried using Controller, but it required maintaining a list of components (like array/object fields) that don’t need to be controlled to avoid unnecessary re-renders. Ideally, components should be able to decide whether they want to receive controlled state updates or not.


  • Instead, I added a custom useField hook that returns the necessary field methods and passed it to component via props. Components can opt-in only when needed, keeping it flexible and avoiding re-renders especially for object and array fields.

Ideally, users of this library should use useController or Controller from React Hook Form to create custom components. However, for internal components—and to keep UI packages agnostic of the form library—we need to pass field methods as props. This also allows supporting other form libraries in the future (e.g., TanStack Forms). and also users can access it as it requires no name or other props as they come prefilled (easy to use)

  • Added support for resolver in the AutoForm component. This enables features like trigger and formState.isValid to work correctly, which previously weren't working (added tests to validate it) and it now supports various validation and re-validation modes. In the core package, added a replaceValue utility to convert literal values to undefined instead of removing them. This is necessary for the resolver to work correctly as before and ensures proper focus behavior. (onSubmit data doesn't get affected)

  • Added a custom focus function that ensures array field labels are focused when validation errors occur but no items are appended yet.

  • An optional form prop has been added to AutoForm, allowing a form instance (created via createFormControl) to be passed in externally. This removes the need for onFormInit and makes it easier to use form methods outside the component. (also allows passing additional useForm props via it like validation mode etc).

  • In AutoFormField, Added a useFieldError hook to reduce unnecessary re-renders when errors occur (e.g. during onSubmit).

I’ve updated the @mantine package to work with all these changes, and its tests are passing.

Note: The form prop and useFieldError rely on features like createFormControl and subscribe, introduced in react-hook-form@^7.55.0. So the minimum RHF version needs to be bumped in affected packages.

Give it a try and let me know your thoughts https://github.com/adityacodepublic/autoform/tree/changes-addResolver

adityacodepublic avatar Jul 16 '25 12:07 adityacodepublic

usage of new form prop

import { createForm } from "@autoform/react";

const Form = () => {
  const form = createForm();

  const schema = z.object({
    name: z.string(),
    email: z.string().email(),
  });

  const schemaProvider = new ZodProvider(schema);

  return (
      <>
          <AutoForm
            form={form}
            schema={schemaProvider}
            withSubmit
          />
          <button onClick={()=> form.reset()}>Reset</button>
    </>
  );
};

adityacodepublic avatar Jul 16 '25 13:07 adityacodepublic

I am facing same issue in wizard form when go back to prev step select,datepicker value disappear even after defaultvalues is in code.

let me know how can i fix this or i have to update package

iurvish avatar Jul 31 '25 13:07 iurvish

Once this issue is resolved, we can release an update to fix this behavior. For now, please refer #180 and #182

adityacodepublic avatar Aug 01 '25 03:08 adityacodepublic