---
title: "A good way to auto-save on change events"
description: "If you don’t want to drown your application with API calls and updates, but still retain the wonderful auto-save on input change for a web form."
canonical_url: "https://www.tarmac.io/resources/a-good-way-to-auto-save-on-change-events/"
last_updated: "2026-10-07"
---

[Resources](https://www.tarmac.io/resources/) / Engineering

# A good way to auto-save on change events

If you don’t want to drown your application with API calls and updates, but still retain the wonderful auto-save on input change for a web form.

Aleksandar Lazarevski · February 8, 2021 Engineering

If you don’t want to drown your application with API calls and updates, but still retain the wonderful **auto-save on input change** for a web form, there is one great solution already out there. I am just going to try and make the simplest example how it is implemented. So let’s see how to optimise the **auto-save** using the **onChange()** event.

![src: https://unsplash.com/photos/FlHdnPO6dlw](https://www.tarmac.io/_astro/img-1.De2B1JUV_2fEloF.webp)

So the following input Component will trigger an API call on every single character change. If the user types quickly, that means a lot of executions in a short amount of time:

```
import React from 'react';

const MyForm = (props) => {
  const [firstName, setFirstName] = React.useState('');

  const firstNameChange = (e) => {
    const firstNameInput = e.target.value;
    setFirstName(firstNameInput);
    props.apiCall(firstNameInput);
  };

  return (
    <form>
      <input
        type="text"
        name="firstname"
        value={firstName}
        onChange={firstNameChange}
      />
    </form>
  );
};

export default MyForm;
```

The thing we’ve been missing is the\*\*\*\*[**lodash/debounce**](https://lodash.com/docs/4.17.15#debounce) function which helps us **only for once** execute **the last apiCall()** every **N** milliseconds. Because we have a React function component, we should use **debounce**in pair with the [**useCallback()**](https://reactjs.org/docs/hooks-reference.html#usecallback)****React****hook. The implementation should look like the following code:

```
import React from 'react';
import debounce from 'lodash.debounce';

const MyForm = (props) => {
  const [firstname, setFirstname] = React.useState('');

  const debouncedApiCall = React.useCallback(
    debounce((value) => props.apiCall(value), 1500),
    []
  );

  const firstnameChange = (e) => {
    const firstNameInput = e.target.value;
    setFirstname(firstNameInput);
    debouncedApiCall(firstNameInput);
  };
  return (
    <form>
      <input
        type="text"
        name="firstname"
        value={firstname}
        onChange={firstnameChange}
      />
    </form>
  );
};

export default MyForm;
```

The only thing we need to dissect here is the creation of the debounced function

```
const debouncedApiCall = React.useCallback(
    debounce((value) => props.apiCall(value), 1500),
    []
  );
```

The React.useCallback() returns a saved callback. The saved callback is returned by the \*\*debounce((value) => props.apiCall(value), 1500)\*\*which defines that the apiCall function will receive a value passed on from the callback and will execute the **last apiCall(value)** function **after 1500ms** from the **last** **debouncedApiCall()**invocation**.**

The following test will show the expected function calls to the mocked API function. Notice if there is an input like ‘Alex’ to the text input field, instead of calling the api on every character written, it will call it with the full string after 1500mms (mocked milliseconds).

```
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import MyForm from './testLodash';

describe('MyForm Page tests', () => {
  test('passes the debounce and updates "Alex" only', async () => {
    jest.useFakeTimers('modern');
    const mockApiCall = jest.fn((e) => e);
    const result = render(<MyForm apiCall={mockApiCall} />);
    const input = result.container.getElementsByTagName('input')[0];
    fireEvent.change(input, { target: { value: 'A' } });
    fireEvent.change(input, { target: { value: 'Al' } });
    fireEvent.change(input, { target: { value: 'Ale' } });
    fireEvent.change(input, { target: { value: 'Alex' } });
    expect(mockApiCall).toHaveBeenCalledTimes(0);
    jest.advanceTimersByTime(500);
    expect(mockApiCall).toHaveBeenCalledTimes(0);
    jest.advanceTimersByTime(500);
    expect(mockApiCall).toHaveBeenCalledTimes(0);
    jest.advanceTimersByTime(501);
    expect(mockApiCall).toHaveLastReturnedWith('Alex');
    expect(mockApiCall).toHaveBeenCalledTimes(1);
  });
});
```

So by having used the **lodash/debounce** function we’ll end up saving the desired information only once, rather than having a call for every character input by the users and drowning both the client and server side with API requests.

You can use these methods in a more creative way and end up with an optimised app that is fast and responsive as well as having the nice auto-save on text change feature.

Keep reading

## More on Engineering

[

### C4 diagrams your AI agents can actually read

Confluence PNGs are invisible to your coding agents. C4, ERD, and sequence diagrams belong in the repo as Mermaid text, not a picture nobody updates.

August 25, 2026](https://www.tarmac.io/resources/c4-diagrams-for-ai-agents/)[

### A definition of done for humans and agents

Works on my machine is not a definition of done. A field guide to the checks that make done mean done, for human pull requests and AI agents alike.

July 30, 2026](https://www.tarmac.io/resources/definition-of-done-humans-and-agents/)[

### Go, Rust, or Python: picking a backend at scale

When success at scale strains Python, the fix is rarely a rewrite. How to choose Go, Rust, or Zig for the one service that needs it.

July 27, 2026](https://www.tarmac.io/resources/go-rust-python-backend-at-scale/)

## Let’s build something worth taking off.

Tell us what you’re building. We’ll assemble the senior team to ship it.

[Let’s team up→](https://www.tarmac.io/contact/?source=resources-a-good-way-to-auto-save-on-change-events)

## Sitemap

See the full [sitemap](https://www.tarmac.io/sitemap.md) for all pages on tarmac.io.
