簡體   English   中英

React Hooks - 即使狀態沒有改變,useEffect 也會觸發

[英]React Hooks - useEffect fires even though the state did not change

我在我的組件中設置了一個效果,如果另一個狀態屬性發生變化,它會改變視圖。 但是由於某種原因,當組件掛載時,即使detailIndex的值沒有改變,效果也會運行。

const EventsSearchList = () => {
    const [view, setView] = useState('table');
    const [detailIndex, setDetailIndex] = useState(null);

    useEffect(() => {
        console.log('onMount', detailIndex);
        // On mount shows "null"
    }, []);


    useEffect(
        a => {
            console.log('Running effect', detailIndex);
            // On mount shows "null"!! Should not have run...
            setView('detail');
        },
        [detailIndex]
    );

    return <div>123</div>;

};

為什么會這樣?

更新:如果不清楚,我正在嘗試在組件更新時運行效果,因為detailIndex更改。 不是在安裝時。

默認情況下,React Hooks 中的useEffect在每次渲染時執行,但您可以在函數中使用第二個參數來定義何時再次執行效果。 這意味着該函數始終在掛載時執行。 在您的情況下,您的第二個useEffect將在開始時和detailIndex更改時運行。

更多信息: https : //reactjs.org/docs/hooks-effect.html

來源:

有經驗的 JavaScript 開發人員可能會注意到,傳遞給 useEffect 的函數在每次渲染時都會有所不同。 [...] 如果某些值在重新渲染之間沒有改變,你可以告訴 React 跳過應用效果。 為此,將數組作為可選的第二個參數傳遞給 useEffect: [...]

就我而言,即使我在 useEffect() 中使用了第二個參數,組件也會不斷更新,並且我正在打印該參數以確保它沒有改變,也沒有改變。 問題是我正在使用 map() 渲染組件,並且在某些情況下鍵發生了變化,如果鍵發生了變化,對於 react 來說,它是一個完全不同的對象。

您可以添加第二個守衛,檢查 detailIndex 是否已從初始值更改為 -1

 useEffect(
    a => {
     if(detailIndex != -1)
      {  console.log('Running effect', detailIndex);
        // On mount shows "null"!! Should not have run...
        setView('detail');
    }},
    [detailIndex]);

當且僅當useEffect的第二個參數狀態通過引用(不是值)改變時,效果才會觸發。

import React, { useState, useEffect } from 'react';
    function App() {
        const [x, setX] = useState({ a: 1 });
        useEffect(() => {
            console.log('x');
        }, [x]);
        setInterval(() => setX({ a: 1 }), 1000);
        return <div></div>
    }
}
export default App;

在此代碼片段中,日志每秒打印一次,因為每次調用setX({a: 1}) ,都會使用對新對象的新引用更新x狀態。

如果setX({a: 1})setX(1)替換,則效果不會定期觸發。

確保代碼只在“真實”狀態更新而不是在第一次渲染時運行的一種方法是分配一個最初為false的變量,只有在第一次渲染后才變為true ,這個變量自然應該是一個鈎子,所以它將在組件的整個生命周期內被記錄下來。

 const {useEffect, useRef, useState} = React const App = () => { const mountedRef = useRef() // ← the "flag" const [value, setValue] = useState(false) // simulate a state change // with the trick useEffect(() => { if (mountedRef.current){ // ← the trick console.log("trick: changed") } }, [value]) // without the trick useEffect(() => { console.log("regular: changed") }, [value]) // fires once & sets "mountedRef" ref to "true" useEffect(() => { console.log("rendered") mountedRef.current = true // update the state after some time setTimeout(setValue, 1000, true) }, []) return null } ReactDOM.render(<App/>, document.getElementById("root"))
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div>

useEffect總是在初始渲染之后運行。

文檔

useEffect 是否在每次渲染后運行? 是的! 默認情況下,它會在第一次渲染之后和每次更新之后運行。 (我們稍后將討論如何自定義它。)與其考慮“安裝”和“更新”,您可能會發現更容易認為效果發生在“渲染之后”。 React 保證 DOM 在它運行效果時已經更新。

關於您的代碼,由於未指定任何依賴項,因此以下內容只會運行一次( useEffect將可選的依賴項數組作為第二個參數):

useEffect(() => {
  console.log('onMount', detailIndex);
  // On mount shows "null" -> since default value for detailIndex is null as seen above
  // const [detailIndex, setDetailIndex] = useState(null);
}, []);

這將只運行在detailIndex改變(嘗試調用setDetailIndex以前的內useEffect ):

useEffect(() =>
  // ... effect code
}, [detailIndex]);

useEffect 鈎子 API 參考

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM