![](/img/trans.png)
[英]React: addEventListener() returns error when called inside of useEffect hook
[英]useEffect inside custom hook not getting called in correct oreder in React
我在一個名為useCustomHook
useEffect
我在兩個組件(即(第一,第二))中使用這個useCustomHook
,但是只有當First
和Second
組件被渲染時才會調用useEffect
。
例如
我有一個第一個組件
import React,{useState} from 'react'
import useCustomHook from './customHook'
function First(){
console.log("component First rendering")
const [count,setCount]=useState(0)
useCustomHook(count)
return (<div>First component</div>)
}
這是我的第二個組成部分
import React,{useState} from 'react'
import useCustomHook from './customHook'
function Second(){
console.log("component Second rendering")
const [count,setCount]=useState(0)
useCustomHook(count)
return (<div>Second component</div>)
}
這是我的 customHook
import {useEffect} from 'react'
function useCustomHook(count){
console.log("useCustomHook getting called")
useEffect(()=>{
console.log("useEffect gets called") //this function is running after both component rendered
},[count])
}
我的主要 App 組件
import First from './first'
import Second from './second'
function App(){
return (
<div>
<First/>
<Second/>
</div>
)
}
我的控制台 output 是:
1)組件第一次渲染
2) useCustomHook 被調用
3)組件二次渲染
4) useCustomHook 被調用
5) (2) useEffect 被調用
我想知道
為什么第5
行 output 不在第2
行之后,為什么Second
component log 發生在第2
行之后,因為useEffect
應該在First
組件調用useCustomHook
之后但在調用Second
component log 之前調用。 為什么在Second
組件日志之前沒有調用useEffect
中的useCustomHook
。
你的 output 是應該的。
我認為您對 output 感到困惑,因為您認為useEffect
與componentDidMount
相同,但這是不正確的。 它們都是不同的,下面提到了它們之間的幾個重要區別:
(與你的問題有關)
它們都在組件的初始渲染之后調用,但是useEffect
在瀏覽器繪制屏幕之后調用,而componentDidMount
在瀏覽器繪制屏幕之前調用。
(與你的問題無關,請跳至答案末尾)
useEffect
捕獲 state 和 props 而componentDidMount
不這樣做。
考慮以下代碼片段以了解useEffect 捕獲 state 和 props 的含義。
class App extends React.Component { constructor() { super(); this.state = { count: 0 }; } componentDidMount() { setTimeout(() => { console.log('count value = ' + this.state.count); }, 4000); } render() { return ( <div> <p>You clicked the button { this.state.count } times</p> <button onClick={ () => this.setState(prev => ({ count: prev.count + 1 })) }> Increment Counter </button> </div> ); } } ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="root"></div>
function App() { const [count, setCount] = React.useState(0); React.useEffect(() => { setTimeout(() => { console.log('count value = ' + count); }, 4000); }, []) return ( <div> <p>You clicked the button { count } times</p> <button onClick={ () => setCount(count + 1) }> Increment Counter </button> </div> ); } ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="root"></div>
這兩個代碼片段是相同的,除了第一個具有基於 class 的組件而第二個具有功能組件。
這兩個片段在 state 中都有一個名為count
的變量,它們都在 4 秒后將count
變量的值記錄到控制台。 它們還包括一個按鈕,可用於增加count
的值。
在控制台記錄count
之前嘗試單擊按鈕(4 或 5 次)。
如果您認為componentDidMount
和useEffect
相同,那么您可能會驚訝地發現這兩個代碼片段在 4 秒后記錄了不同的count
變量值。
基於 Class 的代碼片段記錄最新值,而基於功能組件的代碼片段記錄count
變量的初始值。
他們記錄不同count
變量值的原因是:
this.state
組件中的 this.state 始終指向最新的 state,因此它會在 4 秒后記錄最新的count
。
useEffect
捕獲count
變量的初始值並記錄捕獲的值而不是最新值。
為了深入解釋useEffect
和componentDidMount
之間的區別,我建議你閱讀以下文章
回到你的問題
如果您注意了我回答中與您的問題相關的第一部分,您現在可能明白為什么useEffect
在First
和Second
組件都掛載后運行其回調。
如果沒有,那么讓我解釋一下。
在執行從第First
組件中調用的useCustomHook
function 之后, First
組件被掛載,如果它是一個基於 class 的組件,它的componentDidMount
生命周期 function 將在此時被調用。
First
組件掛載后, Second
組件掛載,如果這也是一個基於 class 的組件,則此時將調用其componentDidMount
生命周期 function。
兩個組件都安裝后,瀏覽器繪制屏幕,結果,您會在屏幕上看到 output。 瀏覽器繪制屏幕后,會為First
和Second
組件執行 useEffect 的回調 function。
簡而言之, useEffect
讓瀏覽器在運行其效果/回調之前繪制屏幕。 這就是為什么useEffect gets called
記錄在 output 的末尾。
你可以在官方文檔上看到更多細節:Timing of effects
如果將First
和Second
組件轉換為 class 組件,則 output 將是:
1. component First rendering
2. component Second rendering
3. component First mounted. // console.log statement inside componentDidMount
4. component Second mounted. // console.log statement inside componentDidMount
您可能希望第 3 行位於第 2 位,第 2 行位於第 3 位,但事實並非如此,因為 React 在將所有子組件插入 DOM 之前以及僅在它們插入 DOM 之后首先執行渲染函數,每個componentDidMount
的 componentDidMount 執行。
如果您創建Third
和Fourth
組件並創建以下 class 組件的層次結構:
App
|__ First
| |__ Third
| |__ Fourth
|
|__ Second
那么您將看到以下 output:
1. First component constructor
2. component First rendering
3. Third component constructor
4. component Third rendering
5. Fourth component constructor
6. component Fourth rendering
7. Second component constructor
8. component Second rendering
9. component Fourth mounted
10. component Third mounted
11. component First mounted
12. component Second mounted
您提到的順序非常合理,這就是掛鈎的工作原理。
流動:
First
組件開始執行。First
組件中,在useCustomHook(count)
行代碼之后,將執行useCustomHook
useCustomHook
中,打印了 console.log 並執行了 useEffect,並且注冊了使用效果所采用的回調,但未執行。First
組件返回 JSX。 即組件已安裝/呈現。First
組件被掛載,就會調用useCustomHook
中的 useEffect 回調。First
組件內的useCustomHook
的范圍僅限於該組件。第二個組件也一樣......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.