gform-react
Back to Guides

Fetching Data

Populate a field from an API on mount with fetch, and refetch when another field changes with fetchDeps.

Some fields need their value from the server - a profile to prefill, a list of options to choose from, or a value that depends on what the user picked in another field. gform-react handles this with two props on GInput: fetch and fetchDeps.

fetch?: (input, fields) => void | Partial<FieldState> | Promise<void | Partial<FieldState>>;
fetchDeps?: string[];

fetch runs once after the field mounts. Whatever it returns (or its Promise resolves to) is merged into that field's state - return { value } to populate the field. Add fetchDeps and it re-runs whenever any listed field's value changes. Both runs are debounced by the field's debounce (default 300ms), and a pending fetch is cancelled if the field unmounts.

Fetching on mount

Put fetch on the field you want to populate and return a partial state. It can be async - return a Promise and gform dispatches the result when it resolves:

ProfileName.tsx
import { GForm, GInput } from "gform-react";
 
async function loadProfile() {
  const res = await fetch("/api/me");
  return res.json(); // { name: "Ada Lovelace" }
}
 
export default function ProfileName() {
  return (
    <GForm onSubmit={(state, e) => { e.preventDefault(); console.log(state.toRawData()); }}>
      <GInput
        formKey="name"
        fetch={async () => {
          const profile = await loadProfile();
          return { value: profile.name }; // merged into the `name` field
        }}
        element={(input, props) => <input {...props} />}
      />
      <button>Save</button>
    </GForm>
  );
}
fetch returns into its own field

The object you return is merged into the field that declares fetch - keyed by its formKey. Return { value } to set the value; you can also return extra keys (like options or a loading flag) and read them back from input in element. To seed several fields at once, prefer GForm's onInit instead.

Try it live

Loading playground…

The field shows a loading state, then fills in once the fake request resolves. The loading flag is just an extra key returned from fetch - it lives on the field state and element reads it from input.

Refetching on change: fetchDeps

List other fields in fetchDeps and fetch re-runs whenever one of their values changes - the classic dependent-field pattern (country → cities, zip → city, currency → rate). Read the dependency from the fields argument:

CountryCity.tsx
<GInput
  formKey="city"
  fetchDeps={["country"]}            // re-run fetch when `country` changes
  fetch={async (input, fields) => {
    const cities = await loadCities(fields.country.value);
    return { options: cities, value: cities[0] }; // populate options + pick the first
  }}
  element={(input, props) => (
    <select {...props}>
      {(input.options || []).map((c) => <option key={c} value={c}>{c}</option>)}
    </select>
  )}
/>

Because fetch also runs on mount, the dependent field loads its initial options right away - then again every time the dependency changes.

fetchDeps is File-aware

Dependency changes are detected by value. File values are compared by name, size, type, and last modified - so a different file re-triggers fetch, but re-selecting the same file does not.

Try it live

Loading playground…

Switch the country - the city list reloads (debounced) and resets to the first option each time.

How it works

  • When it runs. fetch fires in the field's mount effect, and again whenever the fetchDeps signature changes. Each run waits out the field's debounce (default 300ms), so rapid changes collapse into a single fetch.
  • What it returns. Return a Partial field state (or a Promise of one) and gform merges it into the field by formKey - it doesn't replace the field. Return undefined/nothing to fetch as a side effect without changing the field (e.g. update other fields via their own dispatchChanges).
  • Cancellation. If the field unmounts before a debounced fetch fires, gform clears the pending timer - no fetch fires after unmount.
Avoid feedback loops

Don't list a field in its own fetchDeps, and be careful chaining fetches that write into each other's dependencies - each write can re-trigger the next. Keep the dependency direction one-way (e.g. country → city, never both ways).

See GInput for the prop reference and Nested Forms for reading other fields with useFormSelector.