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

时间:2020-03-11 14:33:24

标签: reactjs react-hooks use-effect

有一个ESLint规则exhaustive-deps,它检查Hooks的依赖项数组(例如:useEffect)。我的问题在于,由于现在正在跟踪功能之类的事情,因此更频繁地触发useEffect的内容的影响。
当api调用位于效果内部时,这可能会产生负面影响,因为您只希望一次调用该API。
我可以进一步分解效果,也许对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数组中...

4 个答案:

答案 0 :(得分:0)

由于要对文档herehere做出反应,最好将您的每个条件放在单独的useEffect而不是单个复杂的条件中。每个useEffect都有自己的依赖项数组,并执行特定的工作。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

我很抱歉迟到了。这个仓库就是为了解决这个问题。

react-use-watch

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

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

答案 3 :(得分:-2)

每当传递给它的依赖项发生更改并在其中运行整个代码时,都会调用

useEffect。

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

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