當呼叫 setState
後會發起 component re-render,React 會啟動 reconciliation 流程,最終更新瀏覽器的 DOM 並反映在畫面上。這個過程分為兩個主要階段:Render phase 和 Commit phase。component 內的子孫 component 也會連帶觸發 re-render:
呼叫 setState
更新資料
當呼叫 setState
時,React 並不會立即更新狀態。setState
是一個非同步操作,React 會將這個狀態更新加入一個任務隊列中,並且會自動將多個 setState
操作合併進行批次更新(batch update)。
檢查 state 是否有變化
React 會執行 Object.is(),檢查 state 新舊資料是否有變化。
Object.is()
拿掉會影響 reconciliation 嗎?並不會,Object.is()
就只是個讓效能優化、減少 re-render 的檢查機制,沒有使用 Object.is()
檢查 state 就是直接讓 component 直接進行 re-render。
// This is a React Quiz from BFE.dev
import * as React from 'react'
import { useState, useEffect} from 'react'
import { createRoot } from 'react-dom/client'
function A() {
console.log('A')
return <B/>
}
function B() {
console.log('B')
return <C/>
}
function C() {
console.log('C')
return null
}
function D() {
console.log('D')
return null
}
function App() {
const [state, setState] = useState(0)
useEffect(() => {
setState(state => state + 1)
}, [])
console.log('App')
return (
<div>
<A state={state}/>
<D/>
</div>
)
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
1.App
component 初始渲染:App
component 掛載到瀏覽器後,
console.log('App')
<A state={state}/>
元件,此時的state 0 作為props 傳入子元件<A>
console.log('A')
B/>
元件console.log('B')
<C/>
元件console.log('C')
App
繼續渲染 <D/>
元件console.log('D')
App
re-rendersetState(state => state + 1)
,state 的原始資料是 0,setState
更新值後 state 為 1,資料經 object.is()
檢查後發現有更新故觸發 re-render,重新根據新props 跟 state 產生新的 App 元件。console.log('App')
<A state={state}/>
元件,此時的state 1 作為props 傳入子元件<A>
console.log('A')
B/>
元件console.log('B')
<C/>
元件console.log('C')
App
繼續渲染 <D/>
元件console.log('D')
'App'
'A'
'B'
'C'
'D'
'App'
'A'
'B'
'C'
'D'