ReactNative元件狀態設計思考

cnsnake11發表於2015-12-22

這篇文章寫的較早,是剛接觸RN的時候進行的思考總結,是一個分析的過程,您還可以參考思想更成熟一點的這篇:RN元件架構設計:http://segmentfault.com/a/1190000004161358

設計React元件與設計一個jquery元件或者一個原生js元件最大的區別就是狀態的設計。

術語定義

  1. 屬性

    1. 泛指使用者在初始化元件時候可以傳給元件的

    2. 有些框架也叫options

    3. 是public的

    4. 在RN體系中,叫props,其中還包含了事件

  2. 事件

    1. 是public的

    2. 是function型別的

    3. 在RN體系中使用props來引用

  3. 介面

    1. 是public的

    2. 是function型別的

    3. 通過元件例項化之後的物件,可以進行呼叫

  4. 內部屬性

    1. 是private的

    2. 傳統設計方案中一般用於儲存元件的狀態,資料等

    3. RN體系中沒有對其進行明確,可以自由設計

  5. 狀態

    1. RN體系中明確提出的概念

    2. 傳統設計方案中一般使用內部屬性來表示

傳統設計思路

按照原來設計元件的方法論,一個UI元件對外應該具有屬性、事件、介面,對內具有內部屬性,內部介面。

  1. 屬性就像一份配置檔案,描述了元件應該具有的功能、外觀或者初始狀態。

  2. 事件是元件在工作工程中,當滿足某種條件的時候觸發的回撥函式

  3. 介面是元件物件的方法,可以讓元件去執行某個任務

在原來的設計理念中,並沒有提出元件狀態的概念,但是其也是一直存在的,通常是使用元件私有屬性來儲存。

比如,你設計一個按鈕,那麼他可能有正常狀態,禁用狀態,那麼我們會設計一個屬性disable={true|false}來通知元件初始化的具有的狀態,設計2個介面disable、enable來賦予js有動態改變其狀態的能力,設計2個事件onEnable、onDisable來通知回撥函式元件狀態發生了變化。虛擬碼如下:

//元件定義
class button{

    constructor(disable,pid){//建構函式
    
        this.disable=disable,//屬性:元件初始化使用這個作為狀態
        this.pid=pid;//屬性:父容器的id
        
        this._disable=null,//狀態:私有屬性

        if(this.disable==true){//根據屬性來決定初始化狀態
            this.disable();
        }else{
            this.enable();
        }
        this.render();//渲染元件
    }
        
    
    enable(){
        this._disable=false;
        if(this.el)this.el.set(`disable`,false);
        this.fireEvent(`onEnable`);//觸發事件
    }
    
    disable(){
        this._disable=true;
        if(this.el)this.el.set(`disable`,true);
        this.fireEvent(`onDisable`);//觸發事件
    }
    
    
    render(){
        //渲染元件,狀態直接影響了元件的表現和功能
        $(this.pid).innerHTML=`<button disable=`+this._disable+` />`;
        
        //初始化對dom節點的引用
        this.el=$(this.pid).getChild();
    }
    
}

//父容器
<div id=`a`></div>

//例項化元件並使用
new button(false,`a`);

RN設計思路

上面的示例中,表示了一個傳統UI元件的設計思路,_disable就是這個button元件的關鍵狀態,它直接影響了元件的表現和行為。

而react架構直接把元件狀態提升到了一個新的高度,主要有以下幾點:

  1. 使用固定的介面來宣告元件狀態和狀態預設值

  2. 使用固定的介面來獲得和改變元件狀態的值

  3. 狀態的改變一定會改變元件的表現,會導致元件的重新渲染

前兩項都不是問題,因為我們原來也得有這些東西,只不過寫法發生了變化,關鍵是最後一項。這裡說的不是問題,指的是思路上比較容易接受。這種固定寫法,問題也很明顯,因為要想把一個狀態從RN狀態體系拿進拿出,會產生一定的工作成本,很是不爽。

我一直在思索,元件狀態的變化一定要導致view的變化嗎?

答案肯定是no,因為有些狀態僅僅影響元件的行為,並不影響表現。比如說,我賦予按鈕一個新功能,它可以提交某個表單的資料,也就是需要一個新的狀態formName,他就不影響表現,隻影響行為。

考慮到效能的極致,我們就只能把這種狀態放到RN的狀態體系之外,作為物件的私有屬性存在。

RN號稱diff演算法效能很高,但也不是0損耗,如果對RN的渲染理解很透徹,你當然也可以不這樣設計,但是我這裡還是傾向於保守一些,否則後期的調整也是有工作量的,因為定義、初始化、取值、改值這些操作程式碼都不一樣。寫到這裡我突然想到,如果可以,我們可以通過某種方法方便的修改某個狀態是否影響表現,不用改其它程式碼的話,那麼我們設計狀態的時候就不用區分了,就能讓我們更關注設計本身。

結論

說道這裡,基本可以理清思路了。

元件按照傳統方式設計,該怎麼設計就怎麼設計,在設計原來元件內部屬性的時候,多考慮一步,哪些內部屬性的改變是影響表現的,哪些內部屬性是不影響表現的。將影響表現的放入到RN狀態體系中,將不影響表現的,放入內部屬性中。即可。

tips

  1. 耦合性很強的父子元件,建議將狀態統一放到父元件中,子元件中不要放狀態了,無論是狀態的跨元件共享,還是對view渲染的觸發都很方便。除非你很確定某個狀態只子元件內部使用,通常這種情況後邊可能也會發生變化。

  2. 以下問題可以幫助識別是否是state,摘自官網

    1. 是否是從父級通過 props 傳入的?如果是,可能不是 state 。

    2. 是否會隨著時間改變?如果不是,可能不是 state 。

    3. 能根據元件中其它 state 資料或者 props 計算出來嗎?如果是,就不是 state 。

  3. 根據state的特點進行識別,加入到上一條

    1. 屬性的改變會影響到view的變化就可能是state

  4. 參考地址

    1. https://facebook.github.io/react/docs/thinking-in-react.html

    2. http://wiki.jikexueyuan.com/project/react/thinking-in-react.html

相關文章