2 回答

TA貢獻1853條經驗 獲得超9個贊
您的輸出是應該的。
我認為你對輸出感到困惑,因為你認為這與輸出相同,但這是不正確的。它們都是不同的,它們之間的幾個重要區別如下:useEffect
componentDidMount
它們在不同的時間運行
(與您的問題相關)
它們都是在組件的初始呈現之后調用的,但在瀏覽器繪制屏幕之后調用,而在瀏覽器繪制屏幕之前調用。useEffect
componentDidMount
捕捉道具和狀態
(與您的問題無關,請隨時跳到答案的末尾)
useEffect
捕獲狀態和道具,而不這樣做。componentDidMount
請考慮以下代碼片段,以了解使用效果捕獲狀態和道具的含義。
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>
兩個代碼片段是相同的,除了第一個代碼片段具有基于類的組件,第二個代碼片段具有功能組件。
這兩個代碼段都有一個名為 狀態的變量,它們都在 4 秒后將變量的值記錄到控制臺。它們還包括一個按鈕,可用于遞增 .count
count
count
嘗試單擊按鈕(4 或 5 次),然后將 的值記錄在控制臺上。count
如果您認為這一點并且相同,那么您可能會驚訝地發現兩個代碼片段在4秒后都記錄了不同的變量值。componentDidMount
useEffect
count
基于類的代碼段記錄最新值,而基于函數組件的代碼段記錄變量的初始值。count
它們記錄變量的不同值的原因是:count
this.state
類內部組件始終指向最新狀態,因此它會記錄 4 秒后的最新值。count
useEffect
捕獲變量的初始值,并記錄捕獲的值而不是最新值。count
有關 和 之間的差異的深入說明,我建議您閱讀以下文章useEffect
componentDidMount
回到你的問題
如果您注意了我的答案中與您的問題相關的第一部分,那么您現在可能明白了為什么在安裝和組件后運行其回調。useEffect
First
Second
如果沒有,那么讓我解釋一下。
在執行從組件內部調用的函數后,將掛載組件,如果它是基于類的組件,則此時將調用其生命周期函數。useCustomHook
First
First
componentDidMount
在組件掛載后,組件掛載,如果這也是一個基于類的組件,則此時將調用其生命周期函數。First
Second
componentDidMount
安裝完兩個組件后,瀏覽器將繪制屏幕,因此,您會在屏幕上看到輸出。瀏覽器繪制完屏幕后,將對和組件執行 useEffect 的回調函數。First
Second
簡而言之,讓瀏覽器在運行其效果/回調之前繪制屏幕。這就是為什么記錄在輸出的末尾。useEffect
useEffect gets called
您可以在官方文檔上查看有關此內容的更多詳細信息:效果時間
如果將 和 組件轉換為類組件,則輸出將如下所示:First
Second
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
如果創建 和 組件并創建類組件的以下層次結構:ThirdFourth
App
|__ First
| |__ Third
| |__ Fourth
|
|__ Second
然后,您將獲得以下輸出:
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

TA貢獻1828條經驗 獲得超3個贊
你提到的順序完全有道理,這就是鉤子的工作原理。
流:
First
組件開始執行。在組件中,在代碼行之后,將執行
First
useCustomHook(count)
useCustomHook
在控制臺中.log,并執行 useEffect,并且使用效果采取的回調為“已注冊”和“未執行”。
useCustomHook
First
組件返回 JSX。即組件被安裝/渲染。一旦組件被掛載,就會調用使用效果的回調。
First
useCustomHook
基本上,組件內部的范圍限定為組件。
useCustomHook
First
第二組件也是如此...
添加回答
舉報