欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React useEffect使用教程

 更新時(shí)間:2022年10月22日 09:50:29   作者:YinJie…  
useEffect是react v16.8新引入的特性。我們可以把useEffect hook看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三個(gè)函數(shù)的組合

這篇文章會(huì)假設(shè)你對(duì)useEffectAPI有一定程度的了解。

一、每一次渲染都有它自己的 Props and State

在我們討論 effects 之前,我們需要先討論一下渲染,當(dāng)我們更新 state 的時(shí)候,React會(huì)重新渲染組件。每一次渲染都能拿到獨(dú)立的 state,這個(gè)狀態(tài)值是函數(shù)中的一個(gè)常量。

這里關(guān)鍵的點(diǎn)在于任意一次渲染中的常量都不會(huì)隨著時(shí)間改變。渲染輸出會(huì)變是因?yàn)槲覀兊慕M件被一次次調(diào)用,而每一次調(diào)用引起的渲染中,它包含的值獨(dú)立于其他渲染。

如果 props 和 state 在不同的渲染中是相互獨(dú)立的,那么使用到它們的任何值也是獨(dú)立的(包括事件處理函數(shù))。它們都“屬于”一次特定的渲染。即便是事件處理中的異步函數(shù)調(diào)用“看到”的也是這次渲染中的值。

二、每次渲染都有它自己的Effects

讓我們先看向官網(wǎng)的 useEffect 的例子:

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

effect是如何讀取到最新的count狀態(tài)值的呢?

也許,是某種 watching 機(jī)制類似 vue 中的數(shù)據(jù)響應(yīng)式使得能夠在 effect 函數(shù)內(nèi)更新?也或許是一個(gè)可變的值,React 會(huì)在我們組件內(nèi)部修改它以使我們的 effect 函數(shù)總能拿到最新的值?

都不是。

我們已經(jīng)知道是某個(gè)特定渲染中的常量。事件處理函數(shù)“看到”的是屬于它那次特定渲染中的狀態(tài)值。對(duì)于 effects 也同樣如此:

并不是count的值在“不變”的 effect 中發(fā)生了改變,而是 effect 函數(shù)本身在每一次渲染中都不相同。

React 會(huì)記住你提供的 effect 函數(shù),并且會(huì)在每次更改作用于DOM并讓瀏覽器繪制屏幕后去調(diào)用它。

所以雖然我們說(shuō)的是一個(gè) effect(這里指更新document的title),但其實(shí)每次渲染都是一個(gè)不同的函數(shù)— 并且每個(gè) effect 函數(shù)看到的 props 和 state 都來(lái)自于它屬于的那次特定渲染。

三、關(guān)于依賴項(xiàng)不要對(duì)React撒謊

現(xiàn)在只需要記?。喝绻阍O(shè)置了依賴項(xiàng),effect 中用到的所有組件內(nèi)的值都要包含在依賴中。這包括props,state,函數(shù)組件內(nèi)的任何東西。

在下面這個(gè)組件中,我們的直覺是:“開啟一次定時(shí)器,清除也是一次”。直覺上我們會(huì)設(shè)置依賴為 '[]'。“我只想運(yùn)行一次 effect ”,但是這樣對(duì)嗎?

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <h1>{count}</h1>;
}

我們以為他會(huì)一直遞增下去,但實(shí)際上他只會(huì)遞增一次,你想要觸發(fā)一次因?yàn)樗嵌〞r(shí)器 ,但為什么會(huì)有問(wèn)題?

在第一次渲染中我們執(zhí)行了setCount(0 + 1)。但是我們?cè)O(shè)置了[]依賴,effect不會(huì)再重新運(yùn)行,它后面每一秒都會(huì)調(diào)用setCount(0 + 1)。我們對(duì) React 撒謊說(shuō)我們的 effect 不依賴組件內(nèi)的任何值,可實(shí)際上我們的 effect 有依賴。

四、兩種誠(chéng)實(shí)告知依賴的方法

第一種策略是在依賴中包含所有 effect 中用到的組件內(nèi)的值。讓我們?cè)谝蕾囍邪?code>count

useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, [count]);

這在我們大部分初級(jí)開發(fā)者的眼中都沒有什么問(wèn)題,并且程序確實(shí)不會(huì)出任何 bug,現(xiàn)在,每次修改都會(huì)重新運(yùn)行 effect,這能解決問(wèn)題但是我們的定時(shí)器會(huì)在每一次改變后清除和重新設(shè)定。這肯定不是我們想要的結(jié)果。

第二種策略是修改 effect 內(nèi)部的代碼以確保它包含的值只會(huì)在需要的時(shí)候發(fā)生變更。

在這個(gè)場(chǎng)景中,我們其實(shí)并不需要在effect中使用 count。當(dāng)我們想要根據(jù)前一個(gè)狀態(tài)更新狀態(tài)的時(shí)候,我們可以使用的函數(shù)形式:

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

我們需要告知React的僅僅是去遞增狀態(tài) - 不管它現(xiàn)在具體是什么值。注意我們做到了移除依賴,并且沒有撒謊。我們的 effect 不再讀取渲染中的count值。

五、來(lái)自u(píng)seReducer的助攻

如果我們有兩個(gè)互相依賴的狀態(tài),或者我們想基于一個(gè) prop 來(lái)計(jì)算下一次的 state,setCount(c => c + 1)它并不能做到。幸運(yùn)的是,有一個(gè)更強(qiáng)大的姐妹模式,它的名字叫useReducer。

我們先來(lái)修改上面的例子讓它包含兩個(gè)狀態(tài):count 和 step 。我們的定時(shí)器會(huì)每次在 count 上增加一個(gè) step 值:

function Counter() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + step);
    }, 1000);
    return () => clearInterval(id);
  }, [step]);
  return (
    <>
      <h1>{count}</h1>
      <input value={step} onChange={e => setStep(Number(e.target.value))} />
    </>
  );
}

注意我們沒有撒謊。既然我們?cè)?effect 里使用了step,我們就把它加到依賴?yán)?。所以這也是為什么代碼能運(yùn)行正確。

這個(gè)例子目前的行為是修改會(huì)重啟定時(shí)器 - 因?yàn)樗且蕾図?xiàng)之一。在大多數(shù)場(chǎng)景下,這正是你所需要的。清除上一次的effect然后重新運(yùn)行新的effect并沒有任何錯(cuò)。不過(guò),假如我們不想在改變后重啟定時(shí)器,我們?cè)撊绾螐膃ffect中移除對(duì)的依賴呢?

下面這句話我希望你作為一名 react 開發(fā)人員要記下來(lái):

當(dāng)你想更新一個(gè)狀態(tài),并且這個(gè)狀態(tài)更新依賴于另一個(gè)狀態(tài)的值時(shí),你可能需要用useReducer去替換它們。

reducer 可以讓你把組件內(nèi)發(fā)生了什么(actions)和狀態(tài)如何響應(yīng)并更新分開表述。

我們用一個(gè)dispatch依賴去替換 effect 的依賴 step

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;
  useEffect(() => {
    const id = setInterval(() => {
      dispatch({ type: 'tick' });
    }, 1000);
    return () => clearInterval(id);
  }, [dispatch]);
  return (
    <>
      <h1>{count}</h1>
      <input value={step} onChange={e => {
        dispatch({
          type: 'step',
          step: Number(e.target.value)
        });
      }} />
    </>
  );
}
const initialState = {
  count: 0,
  step: 1,
};
function reducer(state, action) {
  const { count, step } = state;
  if (action.type === 'tick') {
    return { count: count + step, step };
  } else if (action.type === 'step') {
    return { count, step: action.step };
  } else {
    throw new Error();
  }
}

你可能會(huì)問(wèn):“這怎么就更好了?”答案是React會(huì)保證dispatch在組件的聲明周期內(nèi)保持不變。所以上面例子中不再需要重新訂閱定時(shí)器。

相比于直接在 effect 里面讀取狀態(tài),它 dispatch 了一個(gè)action來(lái)描述發(fā)生了什么。這使得我們的 effect 和狀態(tài)解耦。我們的 effect 不再關(guān)心怎么更新狀態(tài),它只負(fù)責(zé)告訴我們發(fā)生了什么。更新的邏輯全都交由 reducer 去統(tǒng)一處理。

六、把函數(shù)移到Effects里

一個(gè)典型的誤解是認(rèn)為函數(shù)不應(yīng)該成為依賴。舉個(gè)例子,下面的代碼看上去可以運(yùn)行正常:

function SearchResults() {
  const [data, setData] = useState({ hits: [] });
  async function fetchData() {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=react',
    );
    setData(result.data);
  }
  useEffect(() => {
    fetchData();
  }, []); 
  // ...

需要明確的是,上面的代碼可以正常工作。但這樣做在組件日漸復(fù)雜的迭代過(guò)程中我們很難確保它在各種情況下還能正常運(yùn)行。

如果我們?cè)谀承┖瘮?shù)內(nèi)使用了某些 state 或者 prop:

function SearchResults() {
  const [query, setQuery] = useState('react');
  // Imagine this function is also long
  function getFetchUrl() {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }
  // Imagine this function is also long
  async function fetchData() {
    const result = await axios(getFetchUrl());
    setData(result.data);
  }
  useEffect(() => {
    fetchData();
  }, []);
  // ...
}

如果我們忘記去更新使用這些函數(shù)(很可能通過(guò)其他函數(shù)調(diào)用)的effects的依賴,我們的effects就不會(huì)同步props和state帶來(lái)的變更。這當(dāng)然不是我們想要的。

如果某些函數(shù)僅在effect中調(diào)用,你可以把它們的定義移到effect中:

function SearchResults() {
  // ...
  useEffect(() => {
    // We moved these functions inside!
    function getFetchUrl() {
      return 'https://hn.algolia.com/api/v1/search?query=react';
    }
    async function fetchData() {
      const result = await axios(getFetchUrl());
      setData(result.data);
    }
    fetchData();
  }, []); 
}

這么做有什么好處呢?我們不再需要去考慮這些“間接依賴”。我們的依賴數(shù)組也不再撒謊:在我們的 effect 中確實(shí)沒有再使用組件范圍內(nèi)的任何東西。

如果我們后面修改getFetchUrl去使用狀態(tài) query,我們更可能會(huì)意識(shí)到我們正在effect里面編輯它因此,我們需要把 query添加到effect的依賴?yán)铮?/p>

function SearchResults() {
  const [query, setQuery] = useState('react');
  useEffect(() => {
    function getFetchUrl() {
      return 'https://hn.algolia.com/api/v1/search?query=' + query;
    }
    async function fetchData() {
      const result = await axios(getFetchUrl());
      setData(result.data);
    }
    fetchData();
  }, [query]); 
}

七、我不想把可復(fù)用的函數(shù)放到Effect里

有時(shí)候你可能不想把函數(shù)移入 effect 里。比如,組件內(nèi)有幾個(gè) effect 使用了相同的函數(shù),你不想在每個(gè) effect 里復(fù)制黏貼一遍這個(gè)邏輯。也或許這個(gè)函數(shù)是一個(gè) prop。

在這種情況下你應(yīng)該忽略對(duì)函數(shù)的依賴嗎?這么做是不對(duì)的。再次強(qiáng)調(diào),effects不應(yīng)該對(duì)它的依賴撒謊。通常我們還有更好的解決辦法。一個(gè)常見的誤解是,“函數(shù)從來(lái)不會(huì)改變”。但是這篇文章你讀到現(xiàn)在,你知道這顯然不是事實(shí)。實(shí)際上,在組件內(nèi)定義的函數(shù)每一次渲染都在變。

function SearchResults() {
  function getFetchUrl(query) {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }
  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, []);
  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, []);
}

在這個(gè)例子中,你可能不想把getFetchUrl移到 effects 中,因?yàn)槟阆霃?fù)用邏輯。

另一方面,如果你對(duì)依賴很“誠(chéng)實(shí)”,你可能會(huì)掉到陷阱里。我們的兩個(gè) effects 都依賴 getFetchUrl,而它每次渲染都不同,所以我們的依賴數(shù)組會(huì)變得無(wú)用:

function SearchResults() {
  function getFetchUrl(query) {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }
  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, [getFetchUrl]); 
  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, [getFetchUrl]);
  // ...
}

我們有兩個(gè)更簡(jiǎn)單的解決辦法。

第一個(gè), 如果一個(gè)函數(shù)沒有使用組件內(nèi)的任何值,你應(yīng)該把它提到組件外面去定義,然后就可以自由地在 effects 中使用:

function getFetchUrl(query) {
  return 'https://hn.algolia.com/api/v1/search?query=' + query;
}
function SearchResults() {
  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, []);
  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, []); 
  // ...
}

你不再需要把它設(shè)為依賴,因?yàn)樗鼈儾辉阡秩痉秶鷥?nèi),因此不會(huì)被數(shù)據(jù)流影響。

或者, 你也可以把它包裝成useCallback Hook:

function SearchResults() {
  const getFetchUrl = useCallback((query) => {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }, []);
  useEffect(() => {
    const url = getFetchUrl('react');
    // ... Fetch data and do something ...
  }, [getFetchUrl]);
  useEffect(() => {
    const url = getFetchUrl('redux');
    // ... Fetch data and do something ...
  }, [getFetchUrl]);
  // ...
}

我們用 useCallback 對(duì) getFetchUrl 做了一層緩存,現(xiàn)在只有當(dāng)依賴項(xiàng)變化的時(shí)候,才會(huì)重新執(zhí)行 useCallback 來(lái)返回新的函數(shù),依賴項(xiàng)沒有變化的時(shí)候就算組件 rerender 了,這個(gè)函數(shù)也不會(huì)重新執(zhí)行,這樣我們把 getFetchUrl 作為 useEffect 的依賴就沒問(wèn)題了。

不同于傳遞參數(shù)的方式,現(xiàn)在我們從狀態(tài)中讀取 query:

function SearchResults() {
  const [query, setQuery] = useState('react');
  const getFetchUrl = useCallback(() => {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }, [query]);  
  useEffect(() => {
    const url = getFetchUrl();
    // ... Fetch data and do something ...
  }, [getFetchUrl]);
  // ...
}

如果query保持不變,useCallback也會(huì)保持不變,我們的 effect 也不會(huì)重新運(yùn)行。但是如果修改了 query,useCallback 也會(huì)隨之改變,因此會(huì)重新請(qǐng)求數(shù)據(jù)。這就像你在Excel里修改了一個(gè)單元格的值,另一個(gè)使用它的單元格會(huì)自動(dòng)重新計(jì)算一樣。

到此這篇關(guān)于React useEffect使用教程的文章就介紹到這了,更多相關(guān)React useEffect內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React為 Vue 引入容器組件和展示組件的教程詳解

    React為 Vue 引入容器組件和展示組件的教程詳解

    這篇文章主要介紹了React為 Vue 引入容器組件和展示組件的教程詳解,文中很詳細(xì)的給大家介紹了使用容器組件的原因,需要的朋友可以參考下
    2018-05-05
  • React中的ref屬性的使用示例詳解

    React中的ref屬性的使用示例詳解

    React 提供了 refrefref 屬性,讓我們可以引用組件的實(shí)例或者原生 DOM 元素,使用 refrefref,可以在父組件中調(diào)用子組件暴露出來(lái)的方法,或者調(diào)用原生 element 的 API,這篇文章主要介紹了React中的ref屬性的使用,需要的朋友可以參考下
    2023-04-04
  • React Hooks中模擬Vue生命周期函數(shù)的指南

    React Hooks中模擬Vue生命周期函數(shù)的指南

    React Hooks 提供了一種在函數(shù)組件中使用狀態(tài)和其他 React 特性的方式,而不需要編寫類組件,Vue 的生命周期函數(shù)和 React Hooks 之間有一定的對(duì)應(yīng)關(guān)系,本文給大家介紹了React Hooks中模擬Vue生命周期函數(shù)的指南,需要的朋友可以參考下
    2024-10-10
  • 詳解React?的數(shù)據(jù)流和生命周期

    詳解React?的數(shù)據(jù)流和生命周期

    這篇文章主要介紹了React?的數(shù)據(jù)流和生命周期,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • react中實(shí)現(xiàn)拖拽排序react-dnd功能

    react中實(shí)現(xiàn)拖拽排序react-dnd功能

    這篇文章主要介紹了react中實(shí)現(xiàn)拖拽排序react-dnd功能,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • 詳解如何在React組件“外”使用父組件的Props

    詳解如何在React組件“外”使用父組件的Props

    這篇文章主要介紹了詳解如何在React組件“外”使用父組件的Props,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • React如何使用refresh_token實(shí)現(xiàn)無(wú)感刷新頁(yè)面

    React如何使用refresh_token實(shí)現(xiàn)無(wú)感刷新頁(yè)面

    本文主要介紹了React如何使用refresh_token實(shí)現(xiàn)無(wú)感刷新頁(yè)面,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 關(guān)于React Native使用axios進(jìn)行網(wǎng)絡(luò)請(qǐng)求的方法

    關(guān)于React Native使用axios進(jìn)行網(wǎng)絡(luò)請(qǐng)求的方法

    axios是一個(gè)基于Promise的Http網(wǎng)絡(luò)庫(kù),可運(yùn)行在瀏覽器端和Node.js中,Vue應(yīng)用的網(wǎng)絡(luò)請(qǐng)求基本都是使用它完成的。這篇文章主要介紹了React Native使用axios進(jìn)行網(wǎng)絡(luò)請(qǐng)求,需要的朋友可以參考下
    2021-08-08
  • React?高階組件與Render?Props優(yōu)缺點(diǎn)詳解

    React?高階組件與Render?Props優(yōu)缺點(diǎn)詳解

    這篇文章主要weidajai?介紹了React?高階組件與Render?Props優(yōu)缺點(diǎn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • webpack入門+react環(huán)境配置

    webpack入門+react環(huán)境配置

    webpack是一個(gè)前端資源模塊化管理和打包工具,說(shuō)白了就是方便我們管理自己的常用的一些代碼,比如你開發(fā)中用到sass以及jade同時(shí)用到es6,開發(fā)時(shí)你不可能改動(dòng)某個(gè)地方就挨個(gè)命令去轉(zhuǎn)換再到瀏覽器去看效果,那樣效率是非常低的。所以webpack幫我們省去了那些多余的步驟。
    2017-02-02

最新評(píng)論