使用React 16.8.6(在之前的版本16.8.3上很好),当我试图阻止fetch请求上的无限循环时,我得到了这个错误:

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

我一直无法找到一个解决办法来停止这个无限循环。我想远离使用useReducer()。我确实找到了关于'筋疲力尽-deps' lint规则#14920的讨论[ESLint]反馈,其中一个可能的解决方案是,如果你认为你知道你在做什么,你可以总是// ESLint - disabled -next-line react-hooks/详尽-deps。我对我正在做的事情没有信心,所以我还没有尝试实现它。

我有这个当前的设置,React钩子useEffect连续运行永远/无限循环,唯一的评论是关于useCallback(),我不熟悉。

我目前如何使用useEffect()(我只想在开始时运行一次,类似componentDidMount()):

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

当前回答

React也给出了解决方案。他们建议你使用useCallback,它会返回你函数的一个内存版本:

'fetchBusinesses'函数使useEffect钩子(在NN行)的依赖关系在每次渲染时发生变化。为了解决这个问题,将'fetchBusinesses'定义包装到它自己的useCallback() Hook react-hooks/竭-deps中

useCallback使用起来很简单,因为它与useEffect具有相同的签名。区别在于useCallback返回一个函数。 它看起来是这样的:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* Some stuff */ })
    .catch(() => { /* Some error handling */ })
  }, [/* deps */])
  // We have a first effect that uses fetchBusinesses
  useEffect(() => {
    // Do things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
   // We can have many effects that use fetchBusinesses
  useEffect(() => {
    // Do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);

其他回答

下一行禁用ESLint即可;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

通过这种方式,您使用它就像使用组件挂载(调用一次)一样。

更新

or

const fetchBusinesses = useCallback(() => {
 // Your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// No need to skip the ESLint warning
}, [fetchBusinesses]);

fetchBusinesses将在每次someDeps更改时被调用。

你可以通过传递一个引用来消除这个Es-lint警告:

下面提到的例子,但是你可以在这个链接上观看解决方案:https://www.youtube.com/watch?v=r4A46oBIwZk&t=8s

警告: 第13:8行:反应。useEffect缺少依赖项:'history'和'currentUser?.role'。要么包含它们,要么删除依赖数组react-hooks/ exhaustion -deps

React.useEffect(() => {
    if (currentUser?.role !== "Student") {
        return history.push("/")
    }
}, [])

解决方法: 步骤1:将业务逻辑移到单独的const中。

现在的警告是:React Hook React。useEffect有一个缺失的依赖:' rolecheck '。

const roleChecking = () =>{
   if (currentUser?.role !== "Student") {
        return history.push("/")
    }
}

React.useEffect(() => {
    roleChecking()
}, [])

最后一步是创建对函数的引用:

  const roleRef = React.useRef();

  const roleChecking = () => {
    if (currentUser?.role !== "Student") {
      return history.push("/");
    }
  };
  roleRef.current = roleChecking;

  React.useEffect(() => {
   return roleRef.current();
  }, [currentUser?.role]);

如果你正在创建一个新的应用程序或有足够的灵活性,有非常好的状态管理库的选择。看看《后坐力》。

为了完整起见:

1. (停止工作)使用函数作为useEffect回调

useEffect(fetchBusinesses, [])

2. 在useEffect()中声明函数

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3.使用useCallback()进行记忆

在这种情况下,如果你的函数中有依赖项,你必须将它们包含在useCallback依赖项数组中,如果函数的参数改变,这将再次触发useEffect。此外,这是一大堆样板文件……因此,只需像1中那样将函数直接传递给useEffect。useEffect (fetchBusinesses[])。

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. 函数的默认实参

正如Behnam Azimi所说

这不是最佳实践,但在某些情况下可能有用。

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

5. 创建自定义钩子

创建一个自定义钩子,并在只需要运行一次函数时调用它。它可能更干净。你也可以在需要时返回一个回调来重置重新运行“初始化”。

// customHooks.js
const useInit = (callback, ...args) => {
  const [mounted, setMounted] = useState(false)
  
  const resetInit = () => setMounted(false)

  useEffect(() => {
     if(!mounted) {
        setMounted(true);
        callback(...args);
     }
  },[mounted, callback]);

  return [resetInit]
}

// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
  const [resetInit] = useInit(fetchBusiness, arg1, arg2)

  useEffect(() => {
    resetInit()
  }, [requiresRefetch, resetInit]);

6. 禁用eslint的警告

禁用警告应该是您最后的手段,但是当您这样做时,最好是内联且显式地执行,因为未来的开发人员可能会感到困惑,或者在不知道linting关闭的情况下创建意外的错误

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

如果您没有在除效果之外的任何地方使用fetchBusinesses方法,您可以简单地将其移动到效果中并避免警告

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

然而,如果您在效果之外使用fetchBusinesses,则必须注意两件事

是否有任何问题,你不传递fetchBusinesses作为方法时,它被使用在装载与它的封闭? 你的方法是否依赖于它从封闭包中接收到的一些变量?你不是这样的。 在每次渲染时,fetchBusinesses将被重新创建,因此将它传递给useEffect将会导致问题。因此,如果要将fetchBusinesses传递给依赖数组,首先必须记住它。

总而言之,我想说的是,如果你在useEffect之外使用fetchBusinesses,你可以使用// eslint-disable-next-line react-hooks/ -deps禁用该规则,否则你可以将方法移动到useEffect内部

要禁用规则,可以这样编写

useEffect(() => {
   // other code
   ...
 
   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 

如果在useEffect中使用的变量是在组件内部定义的,或者是作为道具传递给组件的,则会发生此警告。因为你在同一个组件中定义了fetchBusinesses(),而eslint遵循这一规则,你必须将它传递给依赖数组。但在您的情况下,只传递[]也可以

在这种情况下,它将工作,但如果fetchBusinesses在函数中使用setState,并且调用它将重新呈现组件,因此您的fetchBusinesses将改变,因此useEffect将运行,这将创建一个无限循环。因此,仅仅盲目地遵循eslint可能会导致额外的错误。

用例解决方案让eslint满意,使用useCallback和[]。

const memoizedFetchBusinesses=useCallback(
        fetchBusinesses,
        [] // unlike useEffect you always have to pass a dependency array
       )

当组件第一次呈现时,在内存中创建一个名为fetchBusinessess的函数,并创建memoizedFetchBusinesses变量,该变量也在内存中引用相同的函数。

在第一次渲染之后,一个被称为fetchbusiness的函数将再次被创建,但这一次在不同的内存位置,因为我们在useCallback中有[],memoizedFetchBusinesses将在相同的内存中给你原始的fetchBusinesses函数。这里的useCallback将为您提供在组件的第一次呈现中创建的相同的函数引用。

useEffect(()=>{
    memoizedFetchBusinesses();
},[memoizedFetchBusinesses]) 

相反,你可以这样定义函数

const fetchBusinesses = useCallback(() => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  },[]);

然后在useEffect中

useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);