如何防止 useEffect 中的函数被多次触发?

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

有一个 ESLint 规则 exhaustive-deps 检查你的 Hook 的依赖数组(例如:useEffect)。我的问题在于由于现在正在跟踪函数之类的事情而更频繁地触发 useEffect 的内容的影响。
当 api 调用在效果内时,这可能会产生负面影响,因为您只希望 API 被调用一次。
我可以进一步分解 Effect,也许它对 dep 数组有帮助...也许不会...

如何防止内容被触发超过其需要? 对我来说,为了防止对 API 服务的额外调用,我要么需要忽略 lint 规则(不!),要么对我的代码进行更多检查,以及关于状态的本地状态:

防止会话和 api 调用被多次触发的示例:

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,
  ]);

作为旁注,有趣的是,状态的挂钩,如 setSaveRequestError、setSavingForm 没有出现在 dep 数组中...

每当传递给它的依赖项发生变化时,useEffect 就会被调用,并在其中运行整个代码。

注意:useEffect(() => {}, [dependency1, dependency2 ...])

如果您希望仅在组件初始加载时调用 useEffect 中的代码,请将空数组作为依赖项传递。

由于 React 文档 here and here 最好将每个条件放在一个单独的 useEffect 而不是一个复杂的条件中。每个 useEffect 都有自己的依赖数组并做特定的工作。

如果你只是想避免再次执行它(并且你不想传递一个空的 dep 数组)useRef 是你的朋友,因为它不会重新渲染组件(不是状态):

let hasBeenExecuted = useRef(false);
//In effect
if(!hasBeenExecuted.current) {
    // Do stuff
    hasBeenExecuted.current = true
}

对不起,我迟到了。这个仓库就是为了解决这个问题。

react-use-watch

useWatch(() => {
  // your logic
}, [your dependency])

巧妙而完美地解决了这个问题。