搜尋
首頁web前端js教程在React中詳細介紹受控組件與非受控組件

這篇文章主要介紹了淺談React深度編程之受控組件與非受控組件,現在分享給大家,也給大家做個參考。

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

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

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

 <input value="{this.state.value}"/>

 當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 defaultvalue="{this.state.value}"/>

就再不生效,一直是xxxx。

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

var tracker = {
  getValue: function () {
   return currentValue;
  },
  setValue: function (value) {
   currentValue = &#39;&#39; + 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”時,這是由使用者還是框架修改的。

if (!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 defaultValue="foo" />, container);
  ReactDOM.render(<textarea defaultValue="bar" />, container);
  ReactDOM.render(<textarea defaultValue="noise" />, 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 defaultValue="giraffe" />, container);
 
expect(node.value).toBe("giraffe");
 
// _setValue后,gorilla就不能同步到_persistValue,因此还是giraffe
renderTextarea(<textarea defaultValue="gorilla" />, container);
// expect(node.value).toEqual("giraffe");
 
node.value = "cat";
// 这个又是什么回事了呢,因此非监控属性是在diffProps中批量处理的,在监控属性,则是在更后的方法中处理
// 检测到node.value !== _persistValue,于是重写 _persistValue = node.value,于是输出cat
renderTextarea(<textarea defaultValue="monkey" />, 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 value={222}>
    <option value={111}>aaa</option>
    <option value={"222"}>xxx</option>
    <option value={222}>bbb</option>
    <option value={333}>ccc</option>
  </select>,
  container
);
expect(dom.options[2].selected).toBe(true);//选中第三个
//模糊匹配
var dom = ReactDOM.render(
  <select value={222}>
    <option value={111}>aaa</option>
    <option value={"222"}>xxx</option>
    <option value={333}>ccc</option>
  </select>,
  container
);
expect(dom.options[2].selected).toBe(true);//选中第二个

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue.js中如何实现数据分发slot

在Vue中有关使用ajax方法有哪些?

通过vue如何引入公共css文件

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

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
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表達式設定的。現�...

在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?Apr 04, 2025 pm 09:21 PM

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

微信小程序webview中Vue.js動態style位移失效是什麼原因?微信小程序webview中Vue.js動態style位移失效是什麼原因?Apr 04, 2025 pm 09:18 PM

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...

在Tampermonkey中如何實現對多個鏈接的並發GET請求並依次判斷返回結果?在Tampermonkey中如何實現對多個鏈接的並發GET請求並依次判斷返回結果?Apr 04, 2025 pm 09:15 PM

在Tampermonkey中如何對多個鏈接進行並發GET請求並依次判斷返回結果?在Tampermonkey腳本中,我們經常需要對多個鏈...

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尊渡假赌尊渡假赌尊渡假赌

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

MantisBT

MantisBT

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

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。