如果您开始学习 React 或已经有时间使用这个库,那么您肯定会遇到一些与异步函数相关的错误或警告,尤其是使用 hook useEffect
时。
当我在学习这个钩子的功能时,我无法理解为什么要在这个函数中使用 return ,因为在大多数情况下没有必要使用它,没有它 React 也能很好地工作。
随着越来越熟悉 React 的工作方式和组件的生命周期,我开始注意到,在很多情况下,在 hook useEffect
中使用 return 太重要了,尤其是在副作用方面。
副作用可能是从远程服务器获取数据、读取或写入本地存储、设置事件侦听器或设置订阅。单击按钮、提交表单或安装和卸载组件时,可能会出现这些副作用。
React 的useEffect
钩子允许功能组件在组件挂载或某些属性或状态发生变化时执行操作。此挂钩还允许在卸载组件时进行清理。
在 React 中处理副作用是一项中等复杂度的任务。然而,有时,您可能会在组件生命周期(初始渲染、组装、使用、拆卸)和副作用生命周期(开始、进行中、完成)的交叉点遇到困难。
一个这样的困难是当副作用完成并尝试更新已拆卸组件的状态时。
这会导致像这样的 React 警告:
React 应用程序中的内存泄漏主要是由于在卸载组件之前未取消挂载组件时进行的订阅而导致的。
它们会导致许多问题,包括:
因此,有必要消除内存泄漏问题。
它是useEffect
挂钩的一个功能,它允许我们在卸载组件之前停止不再需要执行的副作用。
useEffect
构建方式使我们可以在其中返回一个函数,而这个返回函数就是清理发生的地方。
例如,组件 A 请求 API 获取产品列表,但在发出该异步请求时,组件 A 从 DOM 中删除(已卸载)。不需要完成那个异步请求。
因此,作为改进应用程序的清理方法,您可以清理(取消)异步请求,使其未完成。
useEffect
:
useEffect(() => { // Your effect return () => { // Cleanup } }, [input])
有多种方法可以取消 fetch 请求调用,我们可以使用 fetch AbortController
或 Axios AbortController.
要使用AbortController
,我们必须使用AbortController()
构造函数创建一个控制器。然后,当我们的获取请求启动时,我们将AbortSignal
作为请求的options
对象中的选项传递。
这将控制器和信号与获取请求关联起来,让我们可以随时使用AbortController.abort()
取消它:
useEffect(() => { //create a controller let controller = new AbortController(); (async () => { try { const response = await fetch(API, { // connect the controller with the fetch request signal: controller.signal, }, ); // handle success setList(await response.json()); // remove the controller controller = null; } catch (e) { // Handle the error } })(); //aborts the request when the component umounts return () => controller?.abort(); },[]);
useEffect(() => { // create a controller const controller = new AbortController(); axios .get(API, { signal: controller.signal }) .then({data}) => { // handle success setList(data); }) .catch((err) => { // Handle the error }); //aborts the request when the component umounts return () => controller?.abort(); }, []);
当使用setTimeout(callback, time)
定时器函数时,我们可以使用特殊的clearTimeout(timerId)
函数在卸载时清除它们。
useEffect(() => { let timerId = setTimeout(() => { // do something timerId = null; }, 3000); // cleanup the timmer when component unmout return () => clearTimeout(timerId); }, []);
与超时一样, setIntervals(callback, time)
有一个特殊的功能,可以使用clearInterval(intervalId)
函数清除它们。
useEffect(() => { let intervalId = setInterval(() => { // do something like update the state }, 1000); // cleanup the timer when component unmout return () => clearInterval(interval); }, []);
通过window.removeEventListener
清理监听器。 removeEventListener
调用必须在removeEventListener
调用中引用相同的函数才能正确删除侦听器。
useEffect(() => { // function to add to EventListener const handleKeyUp= (event) => { switch (event.key) { case "Escape": setCollapsed(true); break; } } window.addEventListener("keyup", handleKeyUp); // cleanup the listener when component unmout return () => window.removeEventListener("keyup", handleKeyUp); }, []);
当你创建一个socket.close()
函数中关闭它。
useEffect(() => { const ws = new WebSocket(url, protocols) // do what you want with the socket ws.onmessage = (event) => { setValue(JSON.parse(event.data)); }; // cleanup the web socket when component unmout return () => ws.close() }, [])
我们了解到,一些副作用需要清理以避免内存泄漏和不必要的和不需要的行为。我们必须学会何时以及如何使用useEffect
钩子的清理功能来避免这些问题并优化应用程序。
我建议在卸载组件时清理异步效果。此外,如果异步副作用取决于 prop 或 state 值,那么也可以考虑在组件更新时清理它们。
我希望您觉得本文有用,并且您现在可以正确使用清理功能。
阅读更多:
也发布在这里。