Thomas O
Thomas O

Reputation: 17

Cleanup setTimeout inside useffect async function

I'm developing an autosave component in React.

My logic is whenever the "bestOf" changes, I check with an async fetch function if the data is equal to the data that is stored in my database.

I am now trying to cleanup this timeout in order to not call this function multiple times if multiple modifications to the bestOf occurs but the useEffect cleanup function doesn't work in my case.

Here is my try with clearTimeout

    useEffect(() => {
        let timeout;
        const compareSave = async () => {
            await fetch(`/api/bestof/get?bestof_id=${bestOf.id}`)
                .then((res) => res.json())
                .then((data) => {
                    if (JSON.stringify(data) === JSON.stringify(bestOf)) {
                        return setSave("saved");
                    } else {
                        setSave("unsaved");
                        timeout = setTimeout(() => {
                            handleSave();
                        }, 5000);
                    }
                })
                .catch((err) => console.log(err));
        };

        compareSave();

        return () => {
            clearTimeout(timeout);
        };
    }, [bestOf]);

And here is another try with AbortController

    useEffect(() => {
        const controller = new AbortController();
        const compareSave = async () => {
            await fetch(`/api/bestof/get?bestof_id=${bestOf.id}`, {
                signal: controller.signal,
            })
                .then((res) => res.json())
                .then((data) => {
                    if (JSON.stringify(data) === JSON.stringify(bestOf)) {
                        return setSave("saved");
                    } else {
                        setSave("unsaved");
                        setTimeout(() => {
                            handleSave();
                        }, 5000);
                    }
                })
                .catch((err) => console.log(err));
        };

        compareSave();

        return () => {
            controller.abort()
        };
    }, [bestOf]);

Both solutions doesn't work and the timeout function is executed multiple time for each modification. Any suggestions ?

Upvotes: 1

Views: 482

Answers (1)

Thomas O
Thomas O

Reputation: 17

After a lot of tries i finally found that I need to add a flag to ignore the stuff after fetch in the timeout for if bestOf changes.

Otherwise because it is async, the timeout would be set after I have cleared it.


useEffect(() => {
  let shouldIgnore = false;
  let timeout;

  const compareSave = async () => {
    await fetch(`/api/bestof/get?bestof_id=${bestOf.id}`)
      .then((res) => res.json())
      .then((data) => {
        if (shouldIgnore) {
          return;
        }

        if (JSON.stringify(data) === JSON.stringify(bestOf)) {
          return setSave('saved');
        } else {
          setSave('unsaved');
          timeout = setTimeout(() => {
            handleSave();
          }, 5000);
        }
      })
      .catch((err) => console.log(err));
  };

  compareSave();

  return () => {
    shouldIgnore = true;
    clearTimeout(timeout);
  };
}, [bestOf]);

Upvotes: 1

Related Questions