Portal Community

useAtlasForm

The primary hook. Returns the full form state and the actions used to change it.

import { useAtlasForm } from '@atlas-forms/player-components-react';

interface AtlasFormState {
  // Current field values — snapshot, updated on every change
  values:       Record<string, any>;

  // Validation error messages, keyed by fieldId
  errors:       Record<string, string>;

  // True when any value differs from initialValues
  isDirty:      boolean;

  // True when errors is empty after the last validation run
  isValid:      boolean;

  // True while submitForm() is resolving
  isSubmitting: boolean;

  // Write a single field value and trigger change events
  setFieldValue: (fieldId: string, value: any) => void;

  // Run validation then call the onSubmit prop; sets isSubmitting during execution
  submitForm:    () => Promise<void>;

  // Direct access to the underlying FormEngine
  engine:        FormEngine;
}

// Usage
const MyPlayerInner: React.FC = () => {
  const {
    values,
    errors,
    isDirty,
    isValid,
    isSubmitting,
    setFieldValue,
    submitForm,
    engine,
  } = useAtlasForm();

  // Programmatic field write
  const applyDefaults = () => {
    setFieldValue('currency', 'GBP');
    setFieldValue('vatRate',  0.20);
  };

  // Custom pre-submit action
  const handleAction = async () => {
    const errs = await engine.validate();
    if (Object.keys(errs).length === 0) {
      await submitForm();
    } else {
      console.warn('Validation failed:', errs);
    }
  };

  return (
    <div>
      {isDirty && <p className="unsaved-badge">Unsaved changes</p>}
      {/* ... */}
    </div>
  );
};

useFormVisibility

Returns helpers for evaluating per-control visibility rules and mode visibility settings.

import { useFormVisibility } from '@atlas-forms/player-components-react';

interface FormVisibilityState {
  // Returns true if the control should be rendered in the current mode and value state
  isVisible: (controlId: string) => boolean;

  // Returns all controls whose isVisible() is true — re-evaluated when values change
  getVisibleControls: () => FormControl[];
}

// Usage
const SectionRenderer: React.FC<{ section: FormSection; controls: FormControl[] }> = ({
  section,
  controls,
}) => {
  const { isVisible } = useFormVisibility();

  // Filter once at the section level — do not call isVisible inside each ControlRenderer
  const visibleControls = controls.filter(c => isVisible(c.id));

  if (visibleControls.length === 0) return null;

  return (
    <div className="section">
      <h3>{section.title}</h3>
      {visibleControls.map(c => (
        <ControlRenderer key={c.id} control={c} />
      ))}
    </div>
  );
};
Call useFormVisibility Once Per Component Call useFormVisibility() once at the top of a component and pass isVisible down. Do not call the hook inside a loop or a child component that is rendered many times — each call subscribes to the form context and would generate redundant re-render subscriptions.

useControlDependencies

Returns the dependency graph for a specific control — which controls it reads from, and which controls cascade from it.

import { useControlDependencies } from '@atlas-forms/player-components-react';

interface ControlDependencies {
  // IDs of controls whose values this control reads (via visibilityRule or computed expressions)
  dependsOn:   string[];

  // IDs of controls that re-evaluate when this control's value changes
  cascadesTo:  string[];
}

// Usage — highlight dependent controls in a Studio-style dependency viewer
const DependencyHighlighter: React.FC<{ controlId: string }> = ({ controlId }) => {
  const { dependsOn, cascadesTo } = useControlDependencies(controlId);

  return (
    <div>
      {dependsOn.length > 0 && (
        <p>Reads values from: {dependsOn.join(', ')}</p>
      )}
      {cascadesTo.length > 0 && (
        <p>Triggers re-evaluation of: {cascadesTo.join(', ')}</p>
      )}
    </div>
  );
};

Hook Rules Summary

HookMust be inside FormStateProvider?Re-renders whenTypical use
useAtlasFormYesAny field value, error, or submit state changesSubmit logic, programmatic field writes, isDirty indicator
useFormVisibilityYesAny value change that affects a visibility ruleFilter control lists before rendering
useControlDependenciesYesSchema changes (rare)Design-mode dependency visualisation

Combining All Three Hooks

// A complete custom player inner component using all three hooks
const FullFeaturedInner: React.FC<{ onSubmit: (v: any) => Promise<void> }> = ({ onSubmit }) => {
  const {
    values, errors, isDirty, isSubmitting, submitForm, engine,
  } = useAtlasForm();

  const { isVisible, getVisibleControls } = useFormVisibility();

  const schema   = engine.getSchema();
  const sections = schema.sections ?? [];

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await submitForm();
    if (Object.keys(errors).length === 0) {
      await onSubmit(values);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {isDirty && <div className="unsaved-banner">You have unsaved changes</div>}

      {sections.map(section => {
        const controls = (schema.controls ?? [])
          .filter(c => c.sectionId === section.id && isVisible(c.id));
        if (controls.length === 0) return null;

        return (
          <div key={section.id} className="section-block">
            <h3>{section.title}</h3>
            {controls.map(c => <ControlRenderer key={c.id} control={c} />)}
          </div>
        );
      })}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
};
getVisibleControls for Progress Tracking Use getVisibleControls() to compute a progress percentage — the ratio of visible controls with non-empty values to the total number of visible controls. Invisible controls (hidden by mode or expression) are excluded automatically so your progress bar never counts fields the user cannot see.