Search code examples
reactjsreact-hooksuse-effect

how do you prevent functions inside useEffect from being triggered more than once?


There is an ESLint rule exhaustive-deps which checks the dependency array of your Hooks (ex:useEffect). My question lies around the impact of having the content of the useEffect be triggered more often due to things like functions now being tracked.
This can have a negative impact when an api call is inside an effect because you would only want the API to be called once.
I could break up the Effect more, and maybe it helps with the dep array...maybe not...

How do you prevent content from being triggered more than its needed? For me, in order to prevent the extra calls to API service, I either need to ignore the lint rule (no!) or have more checks in place around my code, as well as have local state about the status:

Example to prevent the session & api call from being triggered more than once:

useEffect(() => {
    if (!saveValues) {  // is this an anti-pattern?
      return;
    }
    if (!sessionId && !wasSaving && savingForm) {
      initSession().then(() => { // 1st call to session service to get Id
        setSendRequest(true); // set local state that it's safe to send API request because I have my session ready
      });
    } else if (sendRequest && sessionId) {
      setSendRequest(false); // immediately set local state back to false so the previous block doesnt fire.
      try {
        const fields = userFields
          .filter(field => !isEmpty(saveValues[field.name]))
          .map(field => {
              name: field.name,
              value,
            };
          );
        insertUser({ sessionId, fields, config, newRoles }) // here's my API call now
          .then(() => saveSession(sessionId)) // 2nd call to session service req'd
      } catch (err) {
        const message = err.message.split('desc = ')[1];
        setSaveRequestError(message);
        setSavingForm(false);
        setSaveModalOpen(false);
        quitSession(sessionId);
      }
    }
    return () => {
      if (sessionId) {
        quitSession(sessionId);
      }
    };
  }, [
    initSession,
    insertUser,
    quitSession,
    roles.rolesList,
    saveSession,
    saveValues,
    savingForm,
    sendRequest,
    sessionId,
    userFields,
    wasSaving,
  ]);

as a side note, interestingly, the hooks for state, like setSaveRequestError, setSavingForm do not show up in dep array...


Solution

  • Due to react documentation here and here it is better to put each of your conditions to a separate useEffect instead of a single complex one. Each useEffect has its own dependency array and do specific job.