如同龍佬!
請參考新主流環境建置筆記。
React 是 JS UI 函式庫,旨在建構單頁、行動裝置軟體。
html 元素構成一樹,VirtualDOM 會批次透過比較當前 DOM 和實際渲染內容的差異,蒐集需要重新渲染的資料並更新之,增加動態渲染元素之效率。
由根走訪 VirtualDOM 樹並傳遞差異,決定哪些枝條要更新。
React 本於所有 HTML 元素都以 JSX 語法表達並生成,所有元素都應繼承 React.Component
物件,該物件有兩重要成員:
今後「元素」表示繼承 React.Component
物件之物件。
props
: 不可變成員,該成員下又有多個不可變成員。
其為元素建構子的參數,於 VirtualDOM 的生成中由親代節點傳遞至子節點,並由子節點持有參考。
props
不可變是對「持有的節點」而言,也就是上述的子節點;對親代節點可變。
至於原因,是因為 props
是親代與子節點的溝通渠道,也就是親代節點對子節點的依賴注入,可用於實作「親代節點發生變化後傳遞至子節點比較差異、重新渲染」的邏輯。若子節點直接更改 props
,親代節點並不會收到重新渲染的通知,引起不可預期的錯誤。
詳細可參見本提問串。
state
: 可變成員。紀錄元素的狀態,其之下可以有多個可變成員。與一般成員不同的是,作為 React 稽查的對象,會觸發 Lifecycle 中 Updating 過程。
state
雖說可變,但應透過 React.Component::setState
方法改變,以觸發生命週期相關函式,而非直接 assign,直接 assign 只發生在建構子。
至於元素的方法?將與 Lifecycle 一併介紹。
所有元素皆有 Lifecycle,即可以用相同的有限狀態機描述。分為三階段,並以物件 React.Component
的形式包裝,所有元素都應繼承之。
上圖粗體表重要方法,以下分別介紹。
render
本方法繪製「一個」HTML 元素。如果想一次繪製很多個元素,需將其包為一個,至少輸出時只能有一個。既然決定元素的外觀,那必然要實作。
回傳值為以下之一。
string
, number
s, boolean
s, null
渲染除了建構時,通常發生在 props
或 state
改變時,透過分析這兩者的重新渲染元素。當某節點要重新渲染時,該節點以降所有子元素都應該重新渲染。
constructor
建構子,初始化物件成員,可不親自實作。但若要實作必須呼叫 super
建構子。
componentDidMount
建立 DOM node,將渲染好的元素加入 DOM tree,可在此時建立網路 request。
componentDidUpdate
可進行 props
和 state
改變的前後檢查,參數主要有二: prevProps
和 prevState
,為走過 update 流程前的成員值。
componentDidUnmount
可拿來取消一些請求,比如元件準備離開 DOM tree 時若正在請求資料,可以取消請求。
不管元素的複雜與否,渲染元素都需要使用上述較為繁雜的物件語法。注意到物件導向的一大特色就是…程式碼比較長 XD,這降低小元件的撰寫速度與可讀性,故新 React 引入本風格,將物件導向必要的成分拆解為數個函式,增加開發效率。
注意「函式」本身似乎是 stateless 的,不過得益於 Javascript 中函式為「頭等函式」(First class function) 的特性,依然可以給函式夾帶變數與函式,稱為 Hooking。
對應
render
函式,不過有差異。
如果說 Class component 風格是「齊全的工具箱」,那 Functional component 便是「易於擴充的工具箱」。前者使用時我們得全面考慮物件的生命週期,並實作相對應的方法們。後者可以由簡入繁,從基本的顯示開始,需要什麼功能再透過 Hooking 擴充元素的功能。
直接渲染用的便是如此,參考官方文件,以下可以快速生成一 div
元素。
useState
: 從 stateless 轉為 stateful對應
this.state
成員和setState
方法。
上圖說明 Functinoal Component 視角下的生命週期。當中 Updating 有 useState
方法,其為直接渲染函式進行擴充,相當於為其賦予 state
成員與 useXXX
方法。
使用上可利用 const
區域變數擷取 useState
的兩個回傳值,前者為 stateful 變數,後者為前者的設定用函式。
先以 Class component 舉例,假設有以下 Class component 實作按鈕累加計數:
只看 class 部分的長度就頭皮發麻了,那麼 Functional 的呢?
兩個完全等價的寫法,顯然後者輕鬆愉悅 OwO
useEffect
: 多功能的 side effect 處理函式對應
componentDidMount
、componentDidUpdate
以及componentWillUnmount
方法。
透過直接呼叫 useEffect
,傳入遇到上述前兩方法所需完成的 callback function。若 useEffect
內的 callback 回傳為一函式,則其等價於 componentWillUnmount
,將在生命週期走入終點時被呼叫。
還是以前者為例,假設要跟去 count 更新 title,如果是 class 版的,利用 Javascript 的函式 assignment 可以簡化實作為以下:
不過 Functional 更勝一籌:
useEffect
被呼叫的時機為「相依變數受到更動」。要了解此,首先看該函式的簽名。
可見第二個參數 (dependency) 可選,根據該參數內容,函式的行為如下 (FN
表某 callback function)
undefined (無傳入) |
empty list | stateful variables | |
---|---|---|---|
效果 | 每次 render 皆呼叫 | 只呼叫一次 | 根據相依變數列表 |
用例 | useEffect(FN) |
useEffect(FN, []) |
useEffect(FN, [var1, var2, ...]) |
Hook 必須屬於 Top level,也就是不可以在以下三種情況下於生成元素之函式內呼叫 useXXX
系列函式:
if-else
, switch
)
生成生成元素之函式的函式:)
比如想將同樣的 state
與 useEffect
方法套用到兩種不同元素上,可以先定義生成者:
接著應用來生成不同元素的版本。