成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

React深度編程:受控組件與非受控組件

wpw / 458人閱讀

摘要:受控組件與非受控組件在官網(wǎng)與國(guó)內(nèi)網(wǎng)上的資料都不多,有些人覺(jué)得它可有可不有,也不在意。受控組件與非受控組件是處理表單的入口。認(rèn)為不能多帶帶存在,需要與等控制的屬性或事件一起使用。它們共同構(gòu)成受控組件,受控是受的控制。

受控組件與非受控組件在官網(wǎng)與國(guó)內(nèi)網(wǎng)上的資料都不多,有些人覺(jué)得它可有可不有,也不在意。這恰恰顯示React的威力,滿足不同規(guī)模大小的工程需求。譬如你只是做ListView這樣簡(jiǎn)單的數(shù)據(jù)顯示,將數(shù)據(jù)拍出來(lái),那么for循壞與{}就足夠了,但后臺(tái)系統(tǒng)存在大量報(bào)表,不同的表單聯(lián)動(dòng),缺了受控組件真的不行。

受控組件與非受控組件是React處理表單的入口。從React的思路來(lái)講,作者肯定讓數(shù)據(jù)控制一切,或者簡(jiǎn)單的理解為,頁(yè)面的生成與更新得忠實(shí)地執(zhí)行JSX的指令。

但是表單元素有其特殊之處,用戶可以通過(guò)鍵盤(pán)輸入與鼠標(biāo)選擇,改變界面的顯示。界面的改變也意味著有一些數(shù)據(jù)被改動(dòng),比較明顯的是input的value,textarea的innerHTML,radio/checkbox的checked,不太明顯的是option的selectedselectedIndex,這兩個(gè)是被動(dòng)修改的。

當(dāng)input.value是由組件的state.value拍出來(lái)的,當(dāng)用戶進(jìn)行輸入修改后,然后JSX再次重刷視圖,這時(shí)input.value是采取用戶的新值還是state的新值?基于這個(gè)分歧,React給出一個(gè)折衷的方案,兩者都支持,于是就產(chǎn)生了今天的主題了。

React認(rèn)為value/checked不能多帶帶存在,需要與onInput/onChange/disabed/readOnly等控制value/checked的屬性或事件一起使用。 它們共同構(gòu)成受控組件,受控是受JSX的控制。如果用戶沒(méi)有寫(xiě)這些額外的屬性與事件,那么框架內(nèi)部會(huì)給它添加一些事件,如onClick, onInput, onChange,阻止你進(jìn)行輸入或選擇,讓你無(wú)法修改它的值。在框架內(nèi)部,有一個(gè)頑固的變量,我稱(chēng)之為 persistValue,它一直保持JSX上次賦給它的值,只能讓內(nèi)部事件修改它。

因此我們可以斷言,受控組件是可通過(guò)事件完成的對(duì)value的控制。

在受控組件中,persistValue總能被刷新。

我們?cè)倏捶鞘芸亟M件,既然value/checked已經(jīng)被占用了,React啟用了HTML中另一組被忽略的屬性defaultValue/defaultChecked。一般認(rèn)為它們是與value/checked相通的,即,value不存在的情況下,defaultValue的值就當(dāng)作是value。

上面我們已經(jīng)說(shuō)過(guò),表單元素的顯示情況是由內(nèi)部的 persistValue 控制的,因此defaultXXX也會(huì)同步persistValue,然后再由persistValue同步DOM。但非受控組件的出發(fā)點(diǎn)是忠實(shí)于用戶操作,如果用戶在代碼中

input.value = "xxxx"

以后

就再不生效,一直是xxxx。

它怎么做到這一點(diǎn),怎么辨識(shí)這個(gè)修改是來(lái)自框架內(nèi)部或外部呢?我翻看了一下React的源碼,原來(lái)它有一個(gè)叫valueTracker的東西跟蹤用戶的輸入

var tracker = {
    getValue: function () {
      return currentValue;
    },
    setValue: function (value) {
      currentValue = "" + value;
    },
    stopTracking: function () {
      detachTracker(node);
      delete node[valueField];
    }
  };
  return tracker;
}

這個(gè)東西又是通過(guò)Object.defineProperty打進(jìn)元素的value/checked的內(nèi)部,因此就知曉用戶對(duì)它的取值賦值操作。

但value/checked還是兩個(gè)很核心的屬性,涉及到太多內(nèi)部機(jī)制(比如說(shuō)value與oninput, onchange, 輸入法事件oncompositionstart,
compositionchange, oncompositionend, onpaste, oncut),為了平緩地修改value/checked,
還要用到Object.getOwnPropertyDescriptor。如果我要兼容IE8,沒(méi)有這么高級(jí)的玩藝兒。我采取另一種更安全的方式,
只用Object.defineProperty修改defaultValue/defaultChecked。

首先我為元素添加一個(gè)_uncontrolled的屬性,用來(lái)表示我已經(jīng)劫持過(guò)defaultXXX。 然后描述對(duì)象 (Object.defineProperty的第三個(gè)參數(shù))的set方法里面再添加一個(gè)開(kāi)關(guān),_observing。在框架內(nèi)部更新視圖,此值為false,更新完,它置為true。

這樣就知曉 input.defaultValue = "xxx"時(shí),這是由用戶還是框架修改的。

f (!dom._uncontrolled) {
    dom._uncontrolled = true;
    inputMonitor.observe(dom, name); //重寫(xiě)defaultXXX的setter/getter
}
dom._observing = false;//此時(shí)是框架在修改視圖,因此需要關(guān)閉開(kāi)關(guān)
dom[name] = val;
dom._observing = true;//打開(kāi)開(kāi)關(guān),來(lái)監(jiān)聽(tīng)用戶的修改行為

inputMonitor的實(shí)現(xiàn)如下

export var inputMonitor = {};
var rcheck = /checked|radio/;
var describe = {
    set: function(value) {
        var controllProp = rcheck.test(this.type) ? "checked" : "value";
        if (this.type === "textarea") {
            this.innerHTML = value;
        }
        if (!this._observing) {
            if (!this._setValue) {
                //defaultXXX只會(huì)同步一次_persistValue
                var parsedValue = (this[controllProp] = value);
                this._persistValue = Array.isArray(value) ? value : parsedValue;
                this._setValue = true;
            }
        } else {
            //如果用戶私下改變defaultValue,那么_setValue會(huì)被抺掉
            this._setValue = value == null ? false : true;
        }
        this._defaultValue = value;
    },
    get: function() {
        return this._defaultValue;
    },
    configurable: true
};

inputMonitor.observe = function(dom, name) {
    try {
        if ("_persistValue" in dom) {
            dom._setValue = true;
        }
        Object.defineProperty(dom, name, describe);
    } catch (e) {}
};

又不小心貼了這么燒腦的代碼,這是碼農(nóng)的壞毛病。不過(guò),到這步,大家都明白,無(wú)論是官方react還是anu/qreact都是通過(guò)Object.defineProperty來(lái)控制用戶的輸入的。

于是我們可以理解以下的代碼的行為了

    var a =  ReactDOM.render(