Skip to content
NewLatest articleDatabricks Lakebase: what it is and how to prepareRead

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.

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

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 function which helps us only for once execute the last apiCall() every N milliseconds. Because we have a React function component, we should use debouncein pair with the useCallback()Reacthook. 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.

Let’s build something worth taking off.

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