React + hooks使用useEffect時加入依賴項會導致無限循環(huán)避矢?

轉:原文鏈接:https://www.zhihu.com/question/479470936/answer/2145462267

這個篇文章(答案)將介紹五種 React useEffect 的 5 種無限循環(huán)模式我注,避免在日常開發(fā)中踩坑停士。

一般來說,無限循環(huán)被認為是不好的做法瞒窒。但在一些邊界 case 中捺僻,你沒有任何選擇,只能選擇無限循環(huán)。了解 React 的無限循環(huán)模式是件好事匕坯。

<noscript>[圖片上傳失敗...(image-72a0de-1661156829278)]

</noscript>

[圖片上傳失敗...(image-2d1725-1661156829278)]

當無限循環(huán)沒有無法停止時束昵,最終瀏覽器會殺死你的代碼正在運行的標簽。所以不要使用沒有任何斷點的 “無限循環(huán)”葛峻。

useEffect

useEffect hook 允許我們在一個組件中表現(xiàn)出副作用锹雏。當 hooks 被引入 react 16 時,useEffect hooks 比其他 hooks 更有吸引力术奖。因為它提供了 componentDidMount礁遵、componentDidUpdatecomponentWillUnmount 生命周期方法的綜合功能。

<noscript>[圖片上傳失敗...(image-e3b0ce-1661156829278)]

</noscript>

[圖片上傳失敗...(image-6c264-1661156829278)]

只有當依賴關系被改變時采记,useEffect hook 才會觸發(fā)回調函數(shù)佣耐。而且它使用比較法來比較 hooks 的值。

你可以把 useEffect 看作是一塊能量石唧龄,它是一塊最強大的石頭兼砖,如果你沒有正確地處理它,這塊石頭會摧毀你既棺。

<noscript>[圖片上傳失敗...(image-d6da54-1661156829278)]

</noscript>

[圖片上傳失敗...(image-a39361-1661156829278)]

1. 缺失依賴

缺失依賴關系的 useEffect 通常被認為是一種不好的做法讽挟,所以總是盡量避免它。

思考一下下面的代碼援制,它將一直調用 API戏挡。

useEffect(() => {
  fetch("/api/user")
    .then((res) => res.json)
    .then((res) => {
      setData(res);
    });
});

會發(fā)生什么

如果 useEffect 只有在依賴關系發(fā)生變化時才觸發(fā)回調,那為什么我們在這里會出現(xiàn)無限循環(huán)晨仑?

你需要考慮到 React 的另一個重要的法則褐墅,即 “當 state 或 props 發(fā)生變化時,組件將重新渲染”洪己。

在這段代碼中妥凳,我們使用 setData 在網(wǎng)絡調用成功后設置狀態(tài)值,它將觸發(fā)組件的重新渲染答捕。由于 useEffect 沒有值可以比較逝钥,所以它將調用回調。

Fetch 將再次觸發(fā)setData拱镐,setData將觸發(fā)組件重新渲染艘款,如此反復。

<noscript>[圖片上傳失敗...(image-33fab6-1661156829277)]

</noscript>

[圖片上傳失敗...(image-cb4e33-1661156829278)]

如何修復這個問題沃琅?

我們需要將依賴指定為空數(shù)組哗咆。

useEffect(() => {
  fetch("/api/user")
    .then((res) => res.json)
    .then((res) => {
      setData(res);
    });
}, []); // <- dependencies

根據(jù)官方文檔,省略依賴關系是不安全的

2. 函數(shù)作為依賴

useEffect 使用淺層對象比較來確定數(shù)據(jù)是否被改變益眉。因為奇怪的 JavaScript 條件判斷系統(tǒng) 晌柬。

var mark1 = function () {
  return "100";
}; // 唯一的對象引用
var mark2 = function () {
  return "100";
}; // 唯一的對象引用
mark1 == mark2; // false
mark1 === mark2; // false

讓我們看看以下代碼

import React, { useCallback, useEffect, useState } from "react";
export default function App() {
  const [count, setCount] = useState(0);
  const getData = () => {
    return window.localStorage.getItem("token");
  };
  const [dep, setDep] = useState(getData());
  useEffect(() => {
    setCount(count + 1);
  }, [getData]);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

函數(shù) getData 作為依賴項被傳入姥份。

當你運行這段代碼時,它將拋出 “超過最大更新” 的錯誤年碘,這意味著代碼有一個無限循環(huán)澈歉。

<noscript>[圖片上傳失敗...(image-fbb8f0-1661156829277)]

</noscript>

[圖片上傳失敗...(image-dc1e62-1661156829278)]

發(fā)生了什么?

由于 useEffect 使用淺層比較法來比較數(shù)值屿衅。該函數(shù)的淺層比較將總是給出 false埃难。

如何修復這個問題?

為了修復這個問題傲诵,我們需要使用另一個叫做 useCallback 的 hook凯砍。

<noscript>[圖片上傳失敗...(image-df769b-1661156829277)]

</noscript>

[圖片上傳失敗...(image-c34a6a-1661156829278)]

useCallback 返回一個 memoized 版本的回調,只在依賴關系改變時才會改變拴竹。

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

3. 將數(shù)組作為依賴

你可能知道悟衩,二者的淺層比較總是假的,所以以數(shù)組形式傳遞依賴關系也會導致 “無限循環(huán)”栓拜。

讓我們看看以下代碼

import React, { useCallback, useEffect, useState } from "react";
export default function App() {
  const [count, setCount] = useState(0);
  const dep = ["a"];
  const [value, setValue] = useState(["b"]);
  useEffect(() => {
    setValue(["c"]);
  }, [dep]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

這里座泳,數(shù)組 dep 作為 useEffect 的依賴傳入。

當你運行這段代碼時幕与,瀏覽器控制臺會拋出這個錯誤挑势。

發(fā)生了什么?

兩個數(shù)組的淺層比較總是假的啦鸣,所以 useEffect 總是會觸發(fā)回調潮饱。

<noscript>[圖片上傳失敗...(image-281739-1661156829277)]

</noscript>

[圖片上傳失敗...(image-40a763-1661156829278)]

如果修復這個問題?

由于 useCallback 的返回是一個函數(shù)诫给,我們不能使用香拉。

那么,我們應該怎么辦中狂?

我們需要使用另一個名為 useRef 的 hook

<noscript>[圖片上傳失敗...(image-190065-1661156829277)]

</noscript>

[圖片上傳失敗...(image-f31632-1661156829278)]

useRef 返回一個可變的對象凫碌,.current 具有初始值

import React, { useEffect, useState, useRef } from "react";
export default function Home() {
  const [value, setValue] = useState(["b"]);
  const { current: a } = useRef(["a"]);
  useEffect(() => {
    setValue(["c"]);
  }, [a]);
}

4. 將對象作為依賴

你可能會猜到兩個對象的淺層比較總是假的胃榕,所以 useEffect 會一直觸發(fā)回調盛险。

讓我們看看一下這段代碼

import React, { useCallback, useEffect, useState } from "react";
export default function App() {
  const [count, setCount] = useState(0);
  const data = {
    is_fetched:false
  };
  useEffect(() => {
    setCount(count + 1);
  }, [data]);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

data 是作為 useEffect 的依賴項傳入的。

當你運行這段代碼時勋又,你的瀏覽器控制臺將被拋出一個無限循環(huán)的錯誤苦掘。

這里發(fā)生了什么?

對象的淺層比較將永遠是假的楔壤,所以它將觸發(fā) useEffect 的回調鹤啡。

<noscript>[圖片上傳失敗...(image-febb7b-1661156829277)]

</noscript>

[圖片上傳失敗...(image-275abd-1661156829278)]

如果修復這個問題?

如果我們將依賴關系備忘化挺邀,我們就能打破無限循環(huán)揉忘。那么,如何做到這一點呢端铛?

是的泣矛,我們將使用另一個名為 useMemo 的 hook。

<noscript>[圖片上傳失敗...(image-24b4b9-1661156829277)]

</noscript>

[圖片上傳失敗...(image-f02146-1661156829278)]

useMemo 只有在依賴關系發(fā)生變化時才會重新計算記憶化的值禾蚕。

import React, { useMemo, useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);
  const data = useMemo(
    () => ({
      is_fetched: false,
    }),
    []
  ); // <- dependencies
  useEffect(() => {
    setCount(count + 1);
  }, [data]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

5. 錯誤的依賴

錯誤的依賴關系與 React 無關您朽,甚至與 javascript 無關。當使用錯誤的依賴關系時换淆,我們必須承擔起責任哗总。

讓我們看看一下這段代碼

import React, { useEffect, useState } from "react";
const App = () => {
  const [text, setText] = useState("");
  useEffect(() => {
    setText(text);
  }, [text]);
  return null;
};
export default App;

我希望沒有必要解釋這個問題模式和它的修復方法。如果你想知道解釋和修復方法倍试,請在評論中告訴我讯屈。

注意:有很多方法可以避免 React 組件內部的無限循環(huán),我只提到了幾種方法县习。

總是使用 eslint-plugin-react-hooks 或 create-react-app涮母,它將幫助你在這些問題進入生產環(huán)境之前找到這些問題。

一家公司在一周內因無限循環(huán)而損失了 $ 7.2 w躁愿。

所以在使用 useEffect 的時候一定要特別小心叛本。

<noscript>[圖片上傳失敗...(image-5f4cf8-1661156829277)]

</noscript>

[圖片上傳失敗...(image-e2607d-1661156829274)]

譯自(掘金翻譯計劃)原文鏈接:https://javascript.plainenglish.io/5-useeffect-infinite-loop-patterns-2dc9d45a253f

編輯于 2021-11-13 11:03

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市彤钟,隨后出現(xiàn)的幾起案子来候,更是在濱河造成了極大的恐慌,老刑警劉巖逸雹,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件营搅,死亡現(xiàn)場離奇詭異,居然都是意外死亡峡眶,警方通過查閱死者的電腦和手機剧防,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辫樱,“玉大人峭拘,你說我怎么就攤上這事∈ㄊ睿” “怎么了鸡挠?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長搬男。 經(jīng)常有香客問我拣展,道長,這世上最難降的妖魔是什么缔逛? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任备埃,我火速辦了婚禮姓惑,結果婚禮上,老公的妹妹穿的比我還像新娘按脚。我一直安慰自己于毙,他們只是感情好,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布辅搬。 她就那樣靜靜地躺著唯沮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪堪遂。 梳的紋絲不亂的頭發(fā)上介蛉,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音溶褪,去河邊找鬼币旧。 笑死,一個胖子當著我的面吹牛猿妈,可吹牛的內容都是我干的佳恬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼于游,長吁一口氣:“原來是場噩夢啊……” “哼毁葱!你這毒婦竟也來了?” 一聲冷哼從身側響起贰剥,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤倾剿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚌成,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體前痘,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年担忧,在試婚紗的時候發(fā)現(xiàn)自己被綠了芹缔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓶盛,死狀恐怖最欠,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情惩猫,我是刑警寧澤芝硬,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站轧房,受9級特大地震影響拌阴,放射性物質發(fā)生泄漏。R本人自食惡果不足惜奶镶,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一迟赃、第九天 我趴在偏房一處隱蔽的房頂上張望陪拘。 院中可真熱鬧,春花似錦纤壁、人聲如沸藻丢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至残黑,卻和暖如春馍佑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梨水。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工拭荤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疫诽。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓舅世,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奇徒。 傳聞我的和親對象是個殘疾皇子雏亚,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容