簡體   English   中英

自定義掛鈎內的 useEffect 未在 React 中以正確的順序調用

[英]useEffect inside custom hook not getting called in correct oreder in React

我在一個名為useCustomHook useEffect我在兩個組件(即(第一,第二))中使用這個useCustomHook ,但是只有當FirstSecond組件被渲染時才會調用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 感到困惑,因為您認為useEffectcomponentDidMount相同,但這是不正確的。 它們都是不同的,下面提到了它們之間的幾個重要區別:

他們在不同的時間運行

(與你的問題有關)

它們都在組件的初始渲染之后調用,但是useEffect在瀏覽器繪制屏幕之后調用,而componentDidMount在瀏覽器繪制屏幕之前調用。

捕獲道具和 state

(與你的問題無關,請跳至答案末尾)

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 次)。

如果您認為componentDidMountuseEffect相同,那么您可能會驚訝地發現這兩個代碼片段在 4 秒后記錄了不同的count變量值。

基於 Class 的代碼片段記錄最新值,而基於功能組件的代碼片段記錄count變量的初始值。

他們記錄不同count變量值的原因是:

  • this.state組件中的 this.state 始終指向最新的 state,因此它會在 4 秒后記錄最新的count

  • useEffect捕獲count變量的初始值並記錄捕獲的值而不是最新值。

為了深入解釋useEffectcomponentDidMount之間的區別,我建議你閱讀以下文章


回到你的問題

如果您注意了我回答中與您的問題相關的第一部分,您現在可能明白為什么useEffectFirstSecond組件都掛載后運行其回調。

如果沒有,那么讓我解釋一下。

在執行從第First組件中調用的useCustomHook function 之后, First組件被掛載,如果它是一個基於 class 的組件,它的componentDidMount生命周期 function 將在此時被調用。

First組件掛載后, Second組件掛載,如果這也是一個基於 class 的組件,則此時將調用其componentDidMount生命周期 function。

兩個組件都安裝后,瀏覽器繪制屏幕,結果,您會在屏幕上看到 output。 瀏覽器繪制屏幕后,會為FirstSecond組件執行 useEffect 的回調 function。

簡而言之, useEffect讓瀏覽器在運行其效果/回調之前繪制屏幕。 這就是為什么useEffect gets called記錄在 output 的末尾。

你可以在官方文檔上看到更多細節:Timing of effects

如果將FirstSecond組件轉換為 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 執行。

如果您創建ThirdFourth組件並創建以下 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.

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