Skip to content
This repository was archived by the owner on May 18, 2026. It is now read-only.

Dynamic Forms #13

Description

@movpushmov

Dynamic Forms RFC

Motivation

In some cases, businesses have requirements that fields in the form appear as a result of interaction with the user, therefore, they will not be known statically (in advance) and need to provide a powerful and user-friendly API to allow developers to create such forms.

New API's

createDynamicForm — creates dynamic form

function createDynamicForm<Schema>(
	options?: CreateFormOptions<Schema>
): DynamicFormType<Fields, Values, Errors>;

field, arrayField, fieldsGroup — for dynamic form schema pre-define

schema: fieldsGroup({
  name: field(),
  email: field(),
  relatives: arrayField(fieldsGroup({
    name: field(),
    passport: field(),
    favoriteTracks: arrayField(fieldsGroup({
      author: field(),
      name: field(),
    }))
  })),
})

DynamicForm — dynamic form type

interface DynamicFormType<Fields, Values, Errors> {
	$fields: Store<Fields>;
	$values: Store<Values>;
	$errors: Store<Errors>;
	
	$isValid: Store<boolean>;
  $isDirty: Store<boolean>;

  $isValidationPending: Store<boolean>;
  
  setValues: EventCallable<Values>;
  setPartialValues: EventCallable<PartialRecursive<Values>>;

  setErrors: EventCallable<ErrorsSchemaPayload>;

  changed: EventCallable<Values>;
  errorsChanged: Event<Errors>;

  validate: EventCallable<void>;
  validated: Event<Values>;
  validatedAndSubmitted: Event<Values>;

  submit: EventCallable<void>;
  submitted: Event<Values>;

  reset: EventCallable<void>;
  clear: EventCallable<void>;
  
  get<Result extends PrimitiveField | ArrayField>(path: string): Result | undefined;
	
	addGroup: EventCallable<{ path: string }>;
	addPrimitiveField: EventCallable<{ path: string, fieldOptions: PrimitiveFieldOptions }>;
	addArrayField: EventCallable<{ path: string, fieldOptions: ArraFieldOptions }>;

	remove: EventCallable<{ path: string }>;

  metaChanged: EventCallable<{ fieldPath: string; meta: any }>;
 
  '@@unitShape': () => {
    values: Store<Values>;
    errors: Store<Errors>;

    isValid: Store<boolean>;
    isDirty: Store<boolean>;
    isValidationPending: Store<boolean>;

    submit: EventCallable<void>;
    validate: EventCallable<void>;

    reset: EventCallable<void>;
    clear: EventCallable<void>;

    setValues: EventCallable<Values>;
    setErrors: EventCallable<ErrorsSchemaPayload>;

    setPartialValues: EventCallable<PartialRecursive<Values>>;
  };
}

API changes

FormType — add get method

Now, in static form, it will also be possible to get a field by key immediately with typings

get<Path extends FormPaths>(path: Path): FormFieldFromPath<Form, Path>;

Examples

const form = createDynamicForm({ schema: {} });

function App() {
  const { fields, addPrimitiveField } = useDynamicForm(form);
	
  return (
    <>
      <p>fields list</p>
			
      {Object.values(fields).map((field) => (
          <input
            onChange={(e) => field.onChange(e.currentTarget.value)}
            value={field.value}
          />
      ))}
			
      <button onClick={() => addPrimitiveField({ path: Math.random().toString(), value: '' })}>
        add field
      </button>
    </>
  );
}

Metadata

Metadata

Assignees

Labels

RFCdocumentationImprovements or additions to documentationenhancementNew feature or request

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions