Search code examples
javascriptreactjslodashreact-hooksdebouncing

How can i implement debounced auto save on input change in React?


So the problem is lets say you have an editor.
The user keeps typing in the editor and he idles for a while like 5 seconds. So after idling for 5 seconds, you made a network request to the api to save what he typed in the database. It should make only one request after 5 seconds of idling.

I got it done but it made the requests which is equal to the amount of words. if you type like asdf , it made four api request. In my example, four console.log();

const EditorComponent = () => {
  const [editorState, setEditorState] = React.useState(
    EditorState.createEmpty()
  );

  // I need another logic which checks the time difference of idling.

  const debounced = () => {
    return debounce(() => {
      console.log("the api is going to call after 5 seconds");
    }, 5000);
  };

  const onEditorStateChange = value => {
    const rawContent = convertToRaw(value.getCurrentContent());
    const markdown = draftToMarkdown(rawContent);
    setEditorState(value);
    debounced()
  };

  return (
    <div style={{ width: "500px" }}>
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={onEditorStateChange}
      />
    </div>
  );
};

export default EditorComponent;

Solution

  • Problem is that a new debounced function is created on every render and hence the APIs are being called multiple times. You must use useCallback to memoize the debounced function. If you want to use editorState within the debounced function, you can pass it on from onEditStateChange method when you call debounced . Also you need to correct your debounce syntax

    const EditorComponent = () => {
      const [editorState, setEditorState] = React.useState(
        EditorState.createEmpty()
      );
    
      // I need another logic that checks the time difference of idling.
    
      const debounced = useCallback(debounce(() => {
          console.log("the api is going to call after 5 seconds");
      }, 5000), []);
    
      const onEditorStateChange = value => {
        const rawContent = convertToRaw(value.getCurrentContent());
        const markdown = draftToMarkdown(rawContent);
        setEditorState(value);
        debounced()
      };
    
      return (
        <div style={{ width: "500px" }}>
          <Editor
            editorState={editorState}
            toolbarClassName="toolbarClassName"
            wrapperClassName="wrapperClassName"
            editorClassName="editorClassName"
            onEditorStateChange={onEditorStateChange}
          />
        </div>
      );
    };
    
    export default EditorComponent;