iT邦幫忙

2024 iThome 鐵人賽

DAY 21
1
佛心分享-刷題不只是刷題

30 天克服前端面試系列 第 21

Day 21 - React 從呼叫 setState 到瀏覽器畫面真的發生改變,中間發生了什麼事情?

  • 分享至 

  • xImage
  •  

當呼叫 setState 後會發起 component re-render,React 會啟動 reconciliation 流程,最終更新瀏覽器的 DOM 並反映在畫面上。這個過程分為兩個主要階段:Render phase 和 Commit phase。component 內的子孫 component 也會連帶觸發 re-render:

React reconciliation 畫面管理流程

  1. 呼叫 setState 更新資料
    當呼叫 setState 時,React 並不會立即更新狀態。setState 是一個非同步操作,React 會將這個狀態更新加入一個任務隊列中,並且會自動將多個 setState 操作合併進行批次更新(batch update)。

  2. 檢查 state 是否有變化
    React 會執行 Object.is(),檢查 state 新舊資料是否有變化。

    • 如果沒有變化,代表資料沒有更新也就不需要更新畫面,接下來的流程就會中斷。
    • 如果有變化,就是資料有更新,接著就會進行 re-render。

Render phase

  1. 重新執行 Component function
    re-render 會執行 component function 並以新版本的 props 和 state 產生新的 react element。
  2. Diffing
    接著,react 會將前一份產生的舊版 react element 與新版 react element 使用 diffing 演算法來找出樹狀結構差異之處。

Commit phase

  1. 更新 DOM
    比較完成後,根據差異的的部分所對應的 DOM element 進行操作, 這個更新過程會在瀏覽器的主執行緒上進行,包括更新 DOM 樹和應用 CSS 樣式,最後更新到瀏覽器 DOM 上。

Object.is() 拿掉會影響 reconciliation 嗎?

並不會,Object.is()就只是個讓效能優化、減少 re-render 的檢查機制,沒有使用 Object.is() 檢查 state 就是直接讓 component 直接進行 re-render。


實例練習

1. React re-render 1

// 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')
  1. App re-render
    執行 useEffect中的 side effet setState(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'

本文同步於此


上一篇
Day 20 -JSX 是什麼?瀏覽器可以直接執行嗎?
下一篇
Day 22 - React componet 的生命週期
系列文
30 天克服前端面試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言