React 中 getDerivedStateFromProps 的用法和反模式

React 的 16.3 版本中對(duì)生命周期進(jìn)行了較大的調(diào)整,這是為了開發(fā)者能正確地使用生命周期,避免誤解其概念而造成反模式。

本文將重點(diǎn)介紹 getDerivedStateFromProps 這個(gè)生命周期。要注意的是,React 16.3 的版本中 getDerivedStateFromProps 的觸發(fā)范圍是和 16.4^ 是不同的,主要區(qū)別是在 setStateforceUpdate 時(shí)會(huì)不會(huì)觸發(fā),具體可以看這個(gè)生命全周期圖
該方法替換了 componentwillmount 和 componentwilllUpdate 使用時(shí)需要考慮好

用法

getDerivedStateFromProps exists for only one purpose. It enables a component to update its internal state as the result of changes in props.

從上邊這句話中,我們可以清晰知道 getDerivedStateFromProps 的作用就是為了讓 props 能更新到組件內(nèi)部 state 中。所以它可能的使用場(chǎng)景有兩個(gè):

  • 無條件的根據(jù) prop 來更新內(nèi)部 state,也就是只要有傳入 prop 值, 就更新 state
  • 只有 prop 值和 state 值不同時(shí)才更新 state 值。

我們接下來看幾個(gè)例子。

假設(shè)我們有個(gè)一個(gè)表格組件,它會(huì)根據(jù)傳入的列表數(shù)據(jù)來更新視圖。

class Table extends React.Component {
    state = {
        list: []
    }
    static getDerivedStateFromProps (props, state) {
        return {
            list: props.list
        }
    }
    render () {
        .... // 展示 list
    }
}
復(fù)制代碼

上面的例子就是第一種使用場(chǎng)景,但是無條件從 prop 中更新 state,我們完全沒必要使用這個(gè)生命周期,直接對(duì) prop 值進(jìn)行操作就好了,無需用 state 值類保存。

在看一個(gè)例子,這個(gè)例子是一個(gè)顏色選擇器,這個(gè)組件能選擇相應(yīng)的顏色并顯示,同時(shí)它能根據(jù)傳入 prop 值顯示顏色。

Class ColorPicker extends React.Component {
    state = {
        color: '#000000'
    }
    static getDerivedStateFromProps (props, state) {
        if (props.color !== state.color) {
            return {
                color: props.color
            }
        }
        return null
    }
    ... // 選擇顏色方法
    render () {
        .... // 顯示顏色和選擇顏色操作
    }
}
復(fù)制代碼

現(xiàn)在我們可以這個(gè)顏色選擇器來選擇顏色,同時(shí)我們能傳入一個(gè)顏色值并顯示。但是這個(gè)組件有一個(gè) bug,如果我們傳入一個(gè)顏色值后,再使用組件內(nèi)部的選擇顏色方法,我們會(huì)發(fā)現(xiàn)顏色不會(huì)變化,一直是傳入的顏色值。

這是使用這個(gè)生命周期的一個(gè)常見 bug。為什么會(huì)發(fā)生這個(gè) bug 呢?在開頭有說到,在 React 16.4^ 的版本中 setStateforceUpdate 也會(huì)觸發(fā)這個(gè)生命周期,所以內(nèi)部 state 變化后,又會(huì)走 getDerivedStateFromProps 方法,并把 state 值更新為傳入的 prop。

接下里我們來修復(fù)這個(gè)bug。

Class ColorPicker extends React.Component {
    state = {
        color: '#000000',
        prevPropColor: ''
    }
    static getDerivedStateFromProps (props, state) {
        if (props.color !== state.prevPropColor) {
            return {
                color: props.color
                prevPropColor: props.color
            }
        }
        return null
    }
    ... // 選擇顏色方法
    render () {
        .... // 顯示顏色和選擇顏色操作
    }
}
復(fù)制代碼

通過保存一個(gè)之前 prop 值,我們就可以在只有 prop 變化時(shí)才去修改 state。這樣就解決上述的問題。

這里小結(jié)下 getDerivedStateFromProps 方法使用的注意點(diǎn):

  • 在使用此生命周期時(shí),要注意把傳入的 prop 值和之前傳入的 prop 進(jìn)行比較。
  • 因?yàn)檫@個(gè)生命周期是靜態(tài)方法,同時(shí)要保持它是純函數(shù),不要產(chǎn)生副作用。

上述的情況在大多數(shù)情況下都是適用,但是這邊還是會(huì)有產(chǎn)生 bug 的風(fēng)險(xiǎn)。具體可以官網(wǎng)提供這個(gè)例子。在 One 和 Two 的默認(rèn)賬號(hào)都相同的情況下,使用同一個(gè)輸入框組件,在切換到 Two,并不會(huì)顯示成 Two 的默認(rèn)賬號(hào)。

這邊解決方法有四種:

第一種是將組件改成完全可控組件(也是狀態(tài)值和方法全由父類控制);

第二種是改成完全不可控組件(也就是組件不接受在 getDerivedStateFromProps 中通過 prop 值來改變內(nèi)部狀態(tài)),然后通過設(shè)置在構(gòu)造函數(shù)中把 prop 傳給 state 和設(shè)置 key 值來處理,因?yàn)?key 變化的時(shí)候 React 會(huì)重新渲染組件,而不是去更新組件。

第三種還是保持上述組件模式,然后通過一個(gè)唯一 ID 來判斷是否更新,而不是通過 color 值來判斷。

第四種不使用 getDerivedStateFromProps,通過 ref 來把改變郵箱的方法暴露出去。

反模式

常見的反模式有兩種,上邊也有提到過。

  • 無條件地根據(jù) prop 值來更新 state 值
  • 當(dāng) prop 值變化并且和 state 不一樣時(shí)就更新 state (會(huì)造成內(nèi)部變化無效,上述也提到過)。

總結(jié)

我們應(yīng)該謹(jǐn)慎地使用 getDerivedStateFromProps 這個(gè)生命周期。我個(gè)人使用情況來說,使用時(shí)要注意下面幾點(diǎn):

  • 因?yàn)檫@個(gè)生命周期是靜態(tài)方法,同時(shí)要保持它是純函數(shù),不要產(chǎn)生副作用。
  • 在使用此生命周期時(shí),要注意把傳入的 prop 值和之前傳入的 prop 進(jìn)行比較(這個(gè) prop 值最好有唯一性,或者使用一個(gè)唯一性的 prop 值來專門比較)。
  • 不使用 getDerivedStateFromProps,可以改成組件保持完全不可控模式,通過初始值和 key 值來實(shí)現(xiàn) prop 改變 state 的情景。

更多詳細(xì)內(nèi)容可以閱讀官網(wǎng) Blog。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容