搜尋
首頁web前端js教程詳解React受控組件與非受控組件

詳解React受控組件與非受控組件

Dec 18, 2017 pm 04:21 PM
react組件

本文主要和大家詳解React受控組件與非受控組件,受控組件與非受控組件在官網與國內網上的資料都不多,有些人覺得它可有可不有,也不在意。這正好顯示React的威力,滿足不同規模大小的工程需求。譬如你只是做ListView這樣簡單的數據顯示,將數據拍出來,那麼for循壞與{}就足夠了,但後台系統存在大量報表,不同的表單聯動,缺了受控組件真的不行。

受控元件與非受控元件是React處理表單的入口。從React的思路來講,作者肯定讓資料控制一切,或者簡單的理解為,頁面的生成與更新得忠實地執行JSX的指令。

但是表單元素有其特殊之處,使用者可以透過鍵盤輸入與滑鼠選擇,改變介面的顯示。介面的改變也意味著有一些資料被改動,比較明顯的是input的value,textarea的innerHTML,radio/checkbox的checked,不太明顯的是option的selectedselectedIndex,這兩個是被動修改的。

<input>

當input.value是由元件的state.value拍出來的,當使用者進行輸入修改後,然後JSX再次重刷視圖,這時input.value是採取使用者的新值還是state的新值?基於這個分歧,React給出一個折衷的方案,兩者都支持,於是就產生了今天的主題了。

React認為value/checked不能單獨存在,需要與onInput/onChange/disabed/readOnly等控制value/checked的屬性或事件一起使用。 它們共同構成受控元件,受控是受JSX的控制。如果使用者沒有寫這些額外的屬性與事件,那麼框架內部會為它增加一些事件,如onClick, onInput, onChange,阻止你進行輸入或選擇,讓你無法修改它的值。在框架內部,有一個頑固的變量,我稱之為 persistValue,它一直保持JSX上次賦給它的值,只能讓內部事件修改它。

因此我們可以斷言,受控元件是可透過事件完成的value的控制。

在受控元件中,persistValue總是能刷新。

我們再看非受控元件,既然value/checked已經被佔用了,React啟用了HTML中另一組被忽略的屬性defaultValue/defaultChecked。一般認為它們是與value/checked相通的,即,value不存在的情況下,defaultValue的值就當作是value。

上面我們已經說過,表單元素的顯示情況是由內部的 persistValue 控制的,因此defaultXXX也會同步persistValue,然後再由persistValue同步DOM。但非受控元件的出發點是忠於使用者操作,如果使用者在程式碼中

input.value = "xxxx"

以後

<input>

就再不生效,一直是xxxx。

它怎麼做到這一點,怎麼辨識這個修改是來自框架內部或外部?我翻看了一下React的源碼,原來它有一個叫valueTracker的東西跟踪用戶的輸入

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

這個東西又是通過Object.defineProperty打進元素的value/checked的內部,因此就知道用戶對它的取值賦值操作。

但value/checked還是兩個很核心的屬性,涉及到太多內部機制(比如說value與oninput, onchange, 輸入法事件oncompositionstart,
compositionchange, oncompositionend, onpaste, oncut),為了平緩地修改value/checked,
還要用到Object.getOwnPropertyDescriptor。如果我要相容IE8,沒有這麼高級的玩藝兒。我採取另一個比較安全的方式,
只用Object.defineProperty修改defaultValue/defaultChecked

首先我要為元素加上一個_uncontrolled的屬性,用來表示我已經劫持過defaultXXX。 然後描述物件 (Object.defineProperty的第三個參數)的set方法裡面再增加一個開關,_observing。在框架內部更新視圖,此值為false,更新完,它置為true。

這樣就知道 input.defaultValue = "xxx"時,這是由使用者還是框架修改的。

f (!dom._uncontrolled) {
    dom._uncontrolled = true;
    inputMonitor.observe(dom, name); //重写defaultXXX的setter/getter
}
dom._observing = false;//此时是框架在修改视图,因此需要关闭开关
dom[name] = val;
dom._observing = true;//打开开关,来监听用户的修改行为

inputMonitor的實作如下

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只会同步一次_persistValue
                var parsedValue = (this[controllProp] = value);
                this._persistValue = Array.isArray(value) ? value : parsedValue;
                this._setValue = true;
            }
        } else {
            //如果用户私下改变defaultValue,那么_setValue会被抺掉
            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) {}
};

又不小心貼了這麼燒腦的程式碼,這是碼農的壞毛病。不過,到這步,大家都明白,無論是官方react或是anu/qreact都是透過Object.defineProperty來控制使用者的輸入的。

於是我們可以理解以下的程式碼的行為了

    var a =  ReactDOM.render(<textarea></textarea>, container);
    ReactDOM.render(<textarea></textarea>, container);
    ReactDOM.render(<textarea></textarea>, container);
    expect(a.defaultValue).toBe("noise");
    expect(a.value).toBe("foo");
    expect(a.textContent).toBe("noise");
    expect(a.innerHTML).toBe("noise");

由於使用者一直沒有手動修改defaultValue,dom._setValue 一直為false/undefined,因此_persistValue 一直可以修改。

另一個例子:

var renderTextarea = function(component, container) {
    if (!container) {
        container = document.createElement("p");
    }
    const node = ReactDOM.render(component, container);
    node.defaultValue = node.innerHTML.replace(/^\n/, "");
    return node;
};

const container = document.createElement("p");
//注意这个方法,用户在renderTextarea中手动改变了defaultValue,_setValue就变成true
const node = renderTextarea(<textarea></textarea>, container);

expect(node.value).toBe("giraffe");

// _setValue后,gorilla就不能同步到_persistValue,因此还是giraffe
renderTextarea(<textarea></textarea>, container);
//  expect(node.value).toEqual("giraffe");

node.value = "cat";
// 这个又是什么回事了呢,因此非监控属性是在diffProps中批量处理的,在监控属性,则是在更后的方法中处理
// 检测到node.value !== _persistValue,于是重写 _persistValue = node.value,于是输出cat
renderTextarea(<textarea></textarea>, container);
expect(node.value).toEqual("cat");

當然表單元素也分許多種,每個表單元素也有其預設行為。

纯文本类:text, textarea, JSX的值,总是往字符串转换
type="number"的控制,值总是为数字,不填或为“”则转换为“0”
radio有联动效果,同一父节点下的相同name的radio控制只能选择一个。
select的value/defaultValue支持数组,不做转换,但用户对底下的option元素做增删操作,selected会跟着变动。

此外select还有模糊匹配与精确匹配之分。

//精确匹配
var dom = ReactDOM.render(
    <select>
        <option>aaa</option>
        <option>xxx</option>
        <option>bbb</option>
        <option>ccc</option>
    </select>,
    container
);
expect(dom.options[2].selected).toBe(true);//选中第三个
//模糊匹配
var dom = ReactDOM.render(
    <select>
        <option>aaa</option>
        <option>xxx</option>
        <option>ccc</option>
    </select>,
    container
);
expect(dom.options[2].selected).toBe(true);//选中第二个

凡此种种,React/anu都是做了大量工作,迷你如preact/react-lite之流则可能遇坑。

相关推荐:

react如何实现菜单权限控制?

React 内部机制探秘

React中组件的写法有哪些


以上是詳解React受控組件與非受控組件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

如何使用Next.js(前端集成)構建多租戶SaaS應用程序如何使用Next.js(前端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

神秘的JavaScript:它的作用以及為什麼重要神秘的JavaScript:它的作用以及為什麼重要Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python還是JavaScript更好?Python還是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

如何安裝JavaScript?如何安裝JavaScript?Apr 05, 2025 am 12:16 AM

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

在Quartz中如何在任務開始前發送通知?在Quartz中如何在任務開始前發送通知?Apr 04, 2025 pm 09:24 PM

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器