React 回憶錄(四)React 中的狀態管理

libinfs發表於2018-07-19

Hi 各位,歡迎來到 React 回憶錄!? 在上一章中,我介紹了使用 React 渲染介面元素的方法,以及在這個過程中蘊含的“元件化”想想。在本章中,我們將把目光聚焦於 React 元件內部的狀態管理,去認識或重新思考以下三個核心概念:

  1. propsstate
  2. 函式元件
  3. 類元件

讓我們開始吧! ?

01. React 中的資料

站在“元件”的角度上,React 把應用中流動的資料分為兩種型別:

  1. 不可更改內容,但可以單向跨元件傳遞的 props
  2. 可以更改內容,但不能跨元件傳遞的 state

進一步說,propsstate 的區別在於,props 是外部的,並且被任何渲染這個元件的程式碼所控制。而 state 則是內部的,並且被元件自身所控制。

非計算機專業的初學者經常困惑 propsstate 在名稱與含義上的關聯,其實大可不必在意,他們本質上只是資料的別稱,只是在 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 使我們迫使大腦關注兩個重要的部分:

  1. 元件看起來是什麼樣?
  2. 元件當前的狀態是什麼?

通過讓元件管理自己的狀態,任何時候狀態的變更都會令 React 自動更新相應的頁面部分。這便是使用 React 構建元件的主要優勢之一:當頁面需要重新渲染時,我們僅僅需要思考的是如何更改狀態。我們不必跟蹤頁面的哪些部分需要更改,不需要決定如何有效的重新呈現頁面,React 自會比較先前的輸出和新的輸出,決定什麼應該發生改變,併為我們做出決定。而這個確定之前改變了什麼和現在應該新輸出什麼的過程有一個專門的名詞,叫做 Reconciliation


04. 修改 state

你應該還記得類元件與函式元件最大的不同在於類元件自身擁有可以改變內部資料的能力。那麼如何行使這一能力呢?和直覺不同,要做到這一點,你需要使用 React 提供的專門的 API:this.setState()

你有兩種方式使用該 API:

  1. 設定物件引數;
  2. 設定函式引數;

讓我們先來看看第一種:

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 被呼叫時實際上做了兩件事:

  1. 修改 state 物件;
  2. 驅動元件重新渲染;

如果你對 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 的兩種資料形式:stateprops,並且介紹了 React 元件的兩種形式:函式元件類元件,希望格外有所收穫,如果有任何問題或建議,也歡迎各位在評論區內留言,下一章見 ?

PS:?如果你對該專題感興趣,別忘了訂閱本專欄,確保及時收到更新通知。記得點選下方?的各個按鈕,讓我知道你認可我的付出,這是激勵我持續產出的動力和源泉 ?。

相關文章