五种有趣的UseEffect无限循环类型-成都创新互联网站建设

关于创新互联

多方位宣传企业产品与服务 突出企业形象

公司简介 公司的服务 荣誉资质 新闻动态 联系我们

五种有趣的UseEffect无限循环类型

一般来说,无限循环被认为是不好的做法。但在一些边界 case 中,你没有任何选择,只能选择无限循环。了解 React 的无限循环模式是件好事。

当无限循环没有无法停止时,最终浏览器会杀死你的代码正在运行的标签。所以不要使用没有任何断点的 “无限循环”。

useEffect

useEffect hook 允许我们在一个组件中表现出副作用。当 hooks 被引入 react 16 时,useEffect hooks 比其他 hooks 更有吸引力。因为它提供了 componentDidMount、componentDidUpdate 和 componentWillUnmount 生命周期方法的综合功能。

只有当依赖关系被改变时,useEffect hook 才会触发回调函数。而且它使用浅比较法来比较 hooks 的值。

你可以把 useEffect 看作是一块能量石,它是一块最强大的石头,如果你没有正确地处理它,这块石头会摧毁你。

缺失依赖

缺失依赖关系的 useEffect 通常被认为是一种不好的做法,所以总是尽量避免它。

思考一下下面的代码,它将一直调用 API。

 
 
 
 
  1. useEffect(() => { 
  2.   fetch("/api/user") 
  3.     .then((res) => res.json) 
  4.     .then((res) => { 
  5.       setData(res); 
  6.     }); 
  7. }); 

会发生什么

如果 useEffect 只有在依赖关系发生变化时才触发回调,那为什么我们在这里会出现无限循环?

你需要考虑到 React 的另一个重要的法则,即 “当 state 或 props 发生变化时,组件将重新渲染”。

在这段代码中,我们使用 setData 在网络调用成功后设置状态值,它将触发组件的重新渲染。由于 useEffect 没有值可以比较,所以它将调用回调。

Fetch 将再次触发setData,setData将触发组件重新渲染,如此反复。

如何修复这个问题?

我们需要将依赖指定为空数组。

 
 
 
 
  1. useEffect(() => { 
  2.   fetch("/api/user") 
  3.     .then((res) => res.json) 
  4.     .then((res) => { 
  5.       setData(res); 
  6.     }); 
  7. }, []); // <- dependencies 

根据官方文档,省略依赖关系是不安全的

依赖性的作用

useEffect 使用浅层对象比较来确定数据是否被改变。因为奇怪的 JavaScript 条件判断系统 ??。

 
 
 
 
  1. var mark1 = function () { 
  2.   return "100"; 
  3. }; // 唯一的对象引用 
  4. var mark2 = function () { 
  5.   return "100"; 
  6. }; // 唯一的对象引用 
  7. mark1 == mark2; // false 
  8. mark1 === mark2; // false 

让我们看看以下代码

 
 
 
 
  1. import React, { useCallback, useEffect, useState } from "react"; 
  2. export default function App() { 
  3.   const [count, setCount] = useState(0); 
  4.   const getData = () => { 
  5.     return window.localStorage.getItem("token"); 
  6.   }; 
  7.   const [dep, setDep] = useState(getData()); 
  8.   useEffect(() => { 
  9.     setCount(count + 1); 
  10.   }, [getData]); 
  11.   return ( 
  12.      
  13.       

    Hello CodeSandbox

     
  14.        setCount(count + 1)}>{count} 
  15.       

    Start editing to see some magic happen!

     
  16.     
 
  •   ); 
  • 函数 getData 作为依赖项被传入。

    当你运行这段代码时,它将抛出 “超过最大更新” 的错误,这意味着代码有一个无限循环。

    发生了什么?

    由于 useEffect 使用浅层比较法来比较数值。该函数的浅层比较将总是给出 false。

    如何修复这个问题?

    为了修复这个问题,我们需要使用另一个叫做 useCallback 的 hook。

    useCallback 返回一个memoized 版本的回调,只在依赖关系改变时才会改变。

     
     
     
     
    1. const getData = useCallback(() => { 
    2.   return window.localStorage.getItem("token"); 
    3. }, []); // <- dependencies 

    将数组作为依赖

    你可能知道,二者的浅层比较总是假的,所以以数组形式传递依赖关系也会导致 “无限循环”。

    让我们看看以下代码

     
     
     
     
    1. import React, { useCallback, useEffect, useState } from "react"; 
    2. export default function App() { 
    3.   const [count, setCount] = useState(0); 
    4.   const dep = ["a"]; 
    5.   const [value, setValue] = useState(["b"]); 
    6.   useEffect(() => { 
    7.     setValue(["c"]); 
    8.   }, [dep]); 
    9.  
    10.   return ( 
    11.      
    12.       

      Hello CodeSandbox

       
    13.        setCount(count + 1)}>{count} 
    14.       

      Start editing to see some magic happen!

       
    15.     
     
  •   ); 
  • 这里,数组 dep 作为 useEffect 的依赖传入。

    当你运行这段代码时,浏览器控制台会抛出这个错误。

    发生了什么?

    两个数组的浅层比较总是假的,所以 useEffect 总是会触发回调。

    如果修复这个问题?

    由于 useCallback 的返回是一个函数,我们不能使用。

    那么,我们应该怎么办?

    我们需要使用另一个名为 useRef 的 hook

    useRef 返回一个可变的对象,.current 具有初始值。

    将对象作为依赖

    你可能会猜到两个对象的浅层比较总是假的,所以 useEffect 会一直触发回调。

    让我们看看一下这段代码

     
     
     
     
    1. import React, { useEffect, useState, useRef } from "react"; 
    2. export default function Home() { 
    3.   const [value, setValue] = useState(["b"]); 
    4.   const { current: a } = useRef(["a"]); 
    5.   useEffect(() => { 
    6.     setValue(["c"]); 
    7.   }, [a]); 

    data 是作为 useEffect 的依赖项传入的。

    当你运行这段代码时,你的浏览器控制台将被抛出一个无限循环的错误。

    这里发生了什么?

    对象的浅层比较将永远是假的,所以它将触发 useEffect 的回调。

    如果修复这个问题?

    如果我们将依赖关系备忘化,我们就能打破无限循环。那么,如何做到这一点呢?

    是的,我们将使用另一个名为 useMemo 的 hook。

    useMemo 只有在依赖关系发生变化时才会重新计算记忆化的值。

     
     
     
     
    1. import React, { useMemo, useEffect, useState } from "react"; 
    2.  
    3. export default function App() { 
    4.   const [count, setCount] = useState(0); 
    5.   const data = useMemo( 
    6.     () => ({ 
    7.       is_fetched: false, 
    8.     }), 
    9.     [] 
    10.   ); // <- dependencies 
    11.   useEffect(() => { 
    12.     setCount(count + 1); 
    13.   }, [data]); 
    14.  
    15.   return ( 
    16.      
    17.       

      Hello CodeSandbox

       
    18.        setCount(count + 1)}>{count} 
    19.       

      Start editing to see some magic happen!

       
    20.     
     
  •   ); 
  • 错误的依赖

    错误的依赖关系与 React 无关,甚至与 javascript 无关。当使用错误的依赖关系时,我们必须承担起责任。

    让我们看看一下这段代码

     
     
     
     
    1. import React, { useEffect, useState } from "react"; 
    2. const App = () => { 
    3.   const [text, setText] = useState(""); 
    4.   useEffect(() => { 
    5.     setText(text); 
    6.   }, [text]); 
    7.   return null; 
    8. }; 
    9. export default App; 

    我希望没有必要解释这个问题模式和它的修复方法。如果你想知道解释和修复方法,请在评论中告诉我。

    注意:有很多方法可以避免 React 组件内部的无限循环,我只提到了几种方法。

    总是使用 eslint-plugin-react-hooks 或 create-react-app,它将帮助你在这些问题进入生产环境之前找到这些问题。

    一家公司在一周内因无限循环而损失了 $ 7.2 w。

    所以在使用 useEffect 的时候一定要特别小心。

    原文:https://javascript.plainenglish.io/5-useeffect-infinite-loop-patterns-2dc9d45a253f

    本文转载自微信公众号「前端铁蛋」,可以通过以下二维码关注。转载本文请联系前端铁蛋公众号。


    标题名称:五种有趣的UseEffect无限循环类型
    链接URL:http://kswsj.cn/article/cdespdi.html

    其他资讯