摘要:受控組件與非受控組件在官網與國內網上的資料都不多,有些人覺得它可有可不有,也不在意。受控組件與非受控組件是處理表單的入口。認為不能多帶帶存在,需要與等控制的屬性或事件一起使用。它們共同構成受控組件,受控是受的控制。
受控組件與非受控組件在官網與國內網上的資料都不多,有些人覺得它可有可不有,也不在意。這恰恰顯示React的威力,滿足不同規模大小的工程需求。譬如你只是做ListView這樣簡單的數據顯示,將數據拍出來,那么for循壞與{}就足夠了,但后臺系統存在大量報表,不同的表單聯動,缺了受控組件真的不行。
受控組件與非受控組件是React處理表單的入口。從React的思路來講,作者肯定讓數據控制一切,或者簡單的理解為,頁面的生成與更新得忠實地執行JSX的指令。
但是表單元素有其特殊之處,用戶可以通過鍵盤輸入與鼠標選擇,改變界面的顯示。界面的改變也意味著有一些數據被改動,比較明顯的是input的value,textarea的innerHTML,radio/checkbox的checked,不太明顯的是option的selected與selectedIndex,這兩個是被動修改的。
當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"
以后
就再不生效,一直是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(, container); ReactDOM.render(, container); ReactDOM.render(, 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("div"); } const node = ReactDOM.render(component, container); node.defaultValue = node.innerHTML.replace(/^ /, ""); return node; }; const container = document.createElement("div"); //注意這個方法,用戶在renderTextarea中手動改變了defaultValue,_setValue就變成true const node = renderTextarea(, container); expect(node.value).toBe("giraffe"); // _setValue后,gorilla就不能同步到_persistValue,因此還是giraffe renderTextarea(, container); // expect(node.value).toEqual("giraffe"); node.value = "cat"; // 這個又是什么回事了呢,因此非監控屬性是在diffProps中批量處理的,在監控屬性,則是在更后的方法中處理 // 檢測到node.value !== _persistValue,于是重寫 _persistValue = node.value,于是輸出cat renderTextarea(, 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( , container ); expect(dom.options[2].selected).toBe(true);//選中第三個
//模糊匹配 var dom = ReactDOM.render( , container ); expect(dom.options[2].selected).toBe(true);//選中第二個
凡此種種,React/anu都是做了大量工作,迷你如preact/react-lite之流則可能遇坑。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/92203.html
摘要:首次發表在個人博客受控組件或都要綁定一個事件每當表單的狀態發生變化都會被寫入組件的中這種組件在中被稱為受控組件在受控組件中組件渲染出的狀態與它的或者向對應通過這種方式消除了組件的局部狀態是的應用的整個狀態可控官方同樣推薦使用受控表單組件總結 首次發表在個人博客 受控組件 { this.setState({ value: e.target.val...
摘要:假如我們從后臺拉取一個數據要填入輸入框,那么必須得使用受控組件,因為非受控組件只能被用戶輸入。不影響正常輸入填充該輸入框的默認值,此時不顯示內容。 網頁中使用的form表單大家肯定都再熟悉不過了,它主要作用是用來收集和提交信息。React中的表單組件與我們普通的Html中的表單及其表現形式沒有什么不同,所以如何使用表單我覺得再拿出來說可能是畫蛇添足、毫無意義。不過再怎么樣也不能辜負大家...
摘要:盤點一下,模式反應了典型的控制權問題。異步狀態管理與控制權提到控制權話題,怎能少得了這樣的狀態管理工具。狀態管理中的控制主義和極簡主義了解了異步狀態中的控制權問題,我們再從全局角度進行分析。 控制權——這個概念在編程中至關重要。比如,輪子封裝層與業務消費層對于控制權的爭奪,就是一個很有意思的話題。這在 React 世界里也不例外。表面上看,我們當然希望輪子掌控的事情越多越好:因為抽象層...
摘要:盤點一下,模式反應了典型的控制權問題。異步狀態管理與控制權提到控制權話題,怎能少得了這樣的狀態管理工具。狀態管理中的控制主義和極簡主義了解了異步狀態中的控制權問題,我們再從全局角度進行分析。 控制權——這個概念在編程中至關重要。比如,輪子封裝層與業務消費層對于控制權的爭奪,就是一個很有意思的話題。這在 React 世界里也不例外。表面上看,我們當然希望輪子掌控的事情越多越好:因為抽象層...
閱讀 2282·2021-11-23 09:51
閱讀 5677·2021-09-22 15:39
閱讀 3354·2021-09-02 15:15
閱讀 3503·2019-08-30 15:54
閱讀 2362·2019-08-30 15:53
閱讀 1403·2019-08-30 14:04
閱讀 2455·2019-08-29 18:33
閱讀 2374·2019-08-29 13:08