Hi 各位,歡迎來到 React 回憶錄!👋 在上一章中,我介紹了使用 React 渲染界面元素的方法,以及在這個過程中蘊含的“組件化”想想。在本章中,我們將把目光聚焦於 React 組件內部的狀態管理,去認識或重新思考以下三個核心概念:
props
和state
- 函數組件
- 類組件
讓我們開始吧! 🙌
01. React 中的數據
站在“組件”的角度上,React 把應用中流動的數據分為兩種類型:
- 不可更改內容,但可以單向跨組件傳遞的
props
; - 可以更改內容,但不能跨組件傳遞的
state
;
進一步說,props
和 state
的區別在於,props
是外部的,並且被任何渲染這個組件的代碼所控制。而 state
則是內部的,並且被組件自身所控制。
非計算機專業的初學者經常困惑
props
和state
在名稱與含義上的關聯,其實大可不必在意,他們本質上只是數據的別稱,只是在 React 中,它們被各自賦予了特殊的限制或能力。
你可以通過組件上的 props
屬性,像在 HTML
中傳遞屬性一樣,將你想要傳遞的任何數據傳遞給子組件,所有的屬性都會被存儲在子組件(類組件)的 this.props
對象中。
function Parent(props) {
return <Child name={"Tom"} />
}
function Child(props) {
return <span>(props.name)</span>
}
02. 函數組件
我們之前提到過,React 使用組件渲染視圖提升性能,而組件即是一個函數,可以用一個公式來簡潔的表示其功用:f(數據) => UI
。到這里我想你應該注意到了,為什么我們說 React 並不是一個大型的 MVC
(或 MVVM
)框架,因為 React 只負責視圖層(View
)的渲染,其他的事情將由 React 生態中的其他工具來完成。
話說回來,對於 React 組件而言,最簡單的一種形式莫過於函數組件了,它充分展現了 React 的哲學,一次只做一件事,組件化和數據驅動UI。
函數組件又稱為“無狀態組件”,“受控組件”或“木偶組件”,因為函數組件只負責接收 props
並返回 UI,它自身並不能擁有可改變的數據,在真實的 React 應用開發場景下,我們經常盡可能的使用函數組件,將整個應用的 UI 拆分成盡可能小的視覺單元。
這是因為函數組件是非常直觀的,它接收屬性返回元素,內部邏輯清晰明確,而且更重要的是,函數組件內沒有 this
關鍵字,因此你永遠不用擔心煩人的“this
上下文問題”。
記住:如果你的組件不需要追蹤內部狀態,盡量使用函數組件。
03. 類組件
和函數組件相對應的,便是“類組件”了,類似的,它也被稱為“有狀態組件”,“非受控組件”和“容器組件”。這里需要注意,雖然我們按照代碼的形式為兩種類型的組件命名,但這並不嚴謹,因為在 JavaScript 中,“類”也是函數。
不同於函數組件,類組件擁有着可以更改的內部數據 -- state
。它最終影響着頁面的渲染情況,而且 state
可以被組件在任何時刻在內部修改。通常的時刻時用戶與界面發生交互的時候。
由於 React 把變化的數據封裝在組件內部,並堅持單向數據流的原則。我們有了高度抽象的 UI 組件,並封裝復雜的業務邏輯。這使得我們可以通過構建,組合一系列小組件開發出大型應用。
那么應該如何向類組件添加 state
呢?很簡單,我們所要做的只是在類組件內部添加一個 state
屬性,state
屬性是一個對象。這個對象代表了組件的狀態,對象的每一個屬性名都代表組件的一個特定的狀態,下面是具體的代碼:
import React from "react"
class Parent extends React.Component {
state = {
name: "Eliot",
}
render() {
return <p>{this.state.name}</p>
}
}
React 使我們迫使大腦關注兩個重要的部分:
- 組件看起來是什么樣?
- 組件當前的狀態是什么?
通過讓組件管理自己的狀態,任何時候狀態的變更都會令 React 自動更新相應的頁面部分。這便是使用 React 構建組件的主要優勢之一:當頁面需要重新渲染時,我們僅僅需要思考的是如何更改狀態。我們不必跟蹤頁面的哪些部分需要更改,不需要決定如何有效的重新呈現頁面,React 自會比較先前的輸出和新的輸出,決定什么應該發生改變,並為我們做出決定。而這個確定之前改變了什么和現在應該新輸出什么的過程有一個專門的名詞,叫做 Reconciliation
。
04. 修改 state
你應該還記得類組件與函數組件最大的不同在於類組件自身擁有可以改變內部數據的能力。那么如何行使這一能力呢?和直覺不同,要做到這一點,你需要使用 React 提供的專門的 API:this.setState()
。
你有兩種方式使用該 API:
- 設置對象參數;
- 設置函數參數;
讓我們先來看看第一種:
this.setState({
name: "Tom"
})
React 會自動合並對 state
的改變。而有時,你的組件需要一個新的 state
,而這個 state
的變化又依賴於舊的 state
值,每當這種時候,你就該使用第二種 API 調用方式:
this.setState((prevState) => ({
name: "mr." + prevState.name
}))
講到這里你可能會感到奇怪,只是更新 state
而已,為什么還需要調用一個專門的 API?我們直接修改之前定義的 state
對象不就好了嗎?之所以這樣設計的原因是,組件內 state
的變化不僅僅是對象屬性值發生變化那么簡單,它還需要驅動整個 UI 進行重新渲染,因此 this.setState()
這個 API 被調用時實際上做了兩件事:
- 修改
state
對象; - 驅動組件重新渲染;
如果你對 React 有一定研究,你可能會質疑我以上所羅列的兩點並不精確,的確如此,小小的 this.setState()
API 其實內部還有很多細節值得注意,例如,當調用 this.setState()
時並不會立即改變 state
的值,也當然不會立即重新渲染組件。例如,當以對象為參數調用 this.setState()
API 時,盡管內部重復為數據賦值,最終的數據也只保留最后一次更改的結果。
不過幸好,這些略顯古怪的狀態早有前人為我們做了詳盡的解釋,如果你感興趣,請點擊下方鏈接查詢更多的信息:
05. 控制組件
當你在 Web 應用中使用表單時,這個表單的數據被存儲於相應的 DOM 節點內部,但正如我們之前提到的,React 的整個關鍵點就在於如何高效的管理應用內的狀態。所以雖然表單的數據被存儲於 DOM 中,React 依然可以對它進行狀態管理。
而管理的方式即是使用“控制組件”。簡單而言,“控制組件”會渲染出一個表單,但是將表單所需的所有真實數據作為 state
存儲於組件內部,而不是 DOM 中。
之所以被稱為“控制組件”的原因也即在於此,“控制組件”控制着組件內的表單數據,因此,唯一更新表單數據的方式就是更新組件內部對應的 state
值。
import React as "react"
class Input extends React.Component {
state = {
value: "enter something...",
}
handleClick: (e) => {
this.setState({value: e.target.value})
}
render() {
<input value={this.state.value} onKeyup={this.handleClick} />
}
}
可以看到,我們使用 handleClick
方法響應用戶每一次鍵盤敲擊以即時更新表單狀態,這樣做不僅天然的支持了即時的輸入驗證,還允許你有條件的禁止或點亮表單按鈕。
06. 小結
這一章我們介紹了 React 的兩種數據形式:state
和 props
,並且介紹了 React 組件的兩種形式:函數組件與類組件,希望格外有所收獲,如果有任何問題或建議,也歡迎各位在評論區內留言,下一章見 🙌
PS:🔊如果你對該專題感興趣,別忘了訂閱本專欄,確保及時收到更新通知。記得點擊下方👇的各個按鈕,讓我知道你認可我的付出,這是激勵我持續產出的動力和源泉 😎。