0

Say I have a request I want to make on a delay, like Google Docs waiting a moment before sending the "save" request, I'd implement it in browser JavaScript by doing something like this:

// Overwrite this global window variable.
window.saveTimeout = false

// Our sample save function.
let save = text => fetch("some arbitrary save request")

// Lightweight scheduler code that runs each time the user
// types something in an input box.
document.querySelector("textarea#autosaveInput").addEventListener("click", event => {
   let text = event.target.value
   clearInterval(window.saveTimeout) // clear
   setTimeout(() => save(text), 5000) // run save after 5 seconds
})

But in React, I've tested a few methods to do this sort of thing without any luck. I've tried:

  • Setting state to a fetch promise.
  • Setting it to window, but that window element seems to be cleared each re-render. Further, window isn't always defined especially in a framework like NextJS.
  • Searching for this, but "overwriting requests" doesn't seem to yield much.

Here's an example React project I'd be implementing along the lines of:

export default function AutosaveInput(props) {
   let onChange = event => {
      let text = event.target.value
      fetch("some arbitrary save request") // calls every change
   }
   return (
      <div>
         <input type="text" onChange={handleSave} />
      </div>
   )
}
Jack Hales
  • 1,574
  • 23
  • 51
  • I'm a little confused about what you're trying to achieve here. Could you explain a little further what you want? – jon doe Jul 04 '22 at 09:19
  • @jondoe in my JS example, you can create snappy autosave logic by overwriting a setTimeout each time the user presses something. That setTimeout is set to trigger after 5 seconds, so after a user does a bunch of typing then stops for 5 seconds, an arbitrary function is called as the timeout expires without being overwritten. I've not been able to implement something similar to this in React. – Jack Hales Jul 05 '22 at 00:22

1 Answers1

1

It seems like you are trying to implement debouncing. There are several ways you can do this. Check out this SO question. I have implemented a simple app which gets input from user and waits 1.5 seconds and then queries a pokemon api with the user's input -

import React, { useEffect, useState } from "react";

function App() {
  const [input, setInput] = useState("");
  const [data, setData] = useState({});
  const [error, setError] = useState("");

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInput(e.target.value);
  };

  useEffect(() => {
    const timeout = setTimeout(() => {
      fetch(`https://pokeapi.co/api/v2/pokemon/${input}`)
        .then((res) => {
          if (!res.ok) {
            throw new Error(`Failed to fetch pokemon ${input}`);
          }
          return res.json();
        })
        .then((data) => {
          setData(data);
          setError("");
        })
        .catch((e) => {
          setError(e.message);
          setData({});
        });
    }, 1500);
    return () => clearTimeout(timeout);
  }, [input]);

  return (
    <div className="App">
      <h1>This is my app</h1>
      <input value={input} onChange={handleChange}></input>
      {data && <p>{JSON.stringify(data, null, 2)}</p>}
      {error && <p>Error - {error}</p>}
    </div>
  );
}

export default App;

working codepen - https://codepen.io/jondoe69420/pen/bGvVMag

jon doe
  • 460
  • 1
  • 7
  • This is great! Thanks heaps for your answer, as I read it it suddenly hit me how it works and this has added to my React knowledge for sure (I've implemented something similar but using setInterval). I'll leave up my question just to help anyone searching for something similar in the future regarding "overwriting requests", as that was the only way I could think to explain the behaviour I wanted. Thanks. – Jack Hales Jul 07 '22 at 08:53