摘要:現在看了源碼之后,發現自己還是,根本就不是這么做的,我還花了很多時間去找他阻止默認行為的證據,結果才發現走了歪路。
不知道大家對于Controlled Input的概念好不好奇,我在最開始用React的時候就對其非常感興趣,然而奈何那時候能力不夠,也沒那么多時間去看源碼,所以一直處于猜測而沒有去證實的階段。在后來使用Vue進行開發的時候,我還自己實現過類似的組件,那時候是通過preventDefault keyDown事件來阻止自動更新,實現了大致的功能,所以當時覺得這可能也是React的做法吧。現在看了源碼之后,發現自己還是too young,React根本就不是這么做的,我還花了很多時間去找他阻止默認行為的證據,結果才發現走了歪路。閑話到此為止,我們來看看React中如何實現Controlled Input
在看這個之前最好了解過React的事件系統,相關內容我已經準備好,但是還沒整理成文章,大家可以先去自行了解下,我更新上來之后會在這里放鏈接,所以也可以關注我獲取最新內容。
先來看一下batchedUpdates的代碼:
function batchedUpdates(fn, bookkeeping) { if (isBatching) { return fn(bookkeeping); } isBatching = true; try { return _batchedUpdates(fn, bookkeeping); } finally { isBatching = false; var controlledComponentsHavePendingUpdates = needsStateRestore(); if (controlledComponentsHavePendingUpdates) { _flushInteractiveUpdates(); restoreStateIfNeeded(); } } }
在finally里面執行了一個方法叫做restoreStateIfNeeded,如果你通過代碼調試把debugger放之前,并且你寫一個不更新state的demo,你會發現,在執行這個方法之前,input里面的內容其實是先變了的,然后執行完之后再變回來。哈哈,現在你應該能大致猜到了吧,是的,React的做法并不關心event,他等內容執行完了,然后看是否需要回滾再滾回來。
那么接下去我們就來看看細節function restoreStateIfNeeded() { if (!restoreTarget) { return; } var target = restoreTarget; var queuedTargets = restoreQueue; restoreTarget = null; restoreQueue = null; restoreStateOfTarget(target); if (queuedTargets) { for (var i = 0; i < queuedTargets.length; i++) { restoreStateOfTarget(queuedTargets[i]); } } }
這里涉及兩個公共變量:restoreTarget和restoreQueue,那么這兩個變量會在什么情況下變化呢?
在同一個文件ReactControlledComponent里面有這么一個方法
export function enqueueStateRestore(target) { if (restoreTarget) { if (restoreQueue) { restoreQueue.push(target); } else { restoreQueue = [target]; } } else { restoreTarget = target; } }
那么這個方法什么時候調用呢?答案是在ChangeEventPlugin里面
ChangeEventPlugin = { extractEvents() { // ... const event = createAndAccumulateChangeEvent( inst, nativeEvent, nativeEventTarget, ); } } function createAndAccumulateChangeEvent(inst, nativeEvent, target) { const event = SyntheticEvent.getPooled( eventTypes.change, inst, nativeEvent, target, ); event.type = "change"; // Flag this event loop as needing state restore. enqueueStateRestore(target); // 這里 accumulateTwoPhaseDispatches(event); return event; }
我們先不關心這里具體是什么含義,我們只需要知道只有change event才需要回滾內容,所以在生成change事件的時候會把對應的節點。而生成事件的時間和batchUpdates是一起的,這就把整個內容串聯起來了
inpute content -> trigger change event -> create event object -> enqueueStateRestore -> events trigger finsihed -> restoreStateIfNeeded如何restore
function restoreStateOfTarget(target) { var internalInstance = getInstanceFromNode(target); if (!internalInstance) { // Unmounted return; } var props = getFiberCurrentPropsFromNode(internalInstance.stateNode); fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); }
在事件系統執行完之后,獲取input節點對應的Fiber對象,然后讀取新的props,這里的props是執行完綁定事件之后的最新的props,這是對應controlled component真正應該顯示的props,然后再去對比props里面的內容和輸入框內的實際內容,如果需要退回就執行。
function restoreControlledState(domElement, tag, props) { switch (tag) { case "input": restoreControlledState(domElement, props); return; case "textarea": restoreControlledState$3(domElement, props); return; case "select": restoreControlledState$2(domElement, props); return; } } function restoreControlledState(element, props) { var node = element; updateWrapper(node, props); updateNamedCousins(node, props); } function updateWrapper(element, props) { var node = element; updateChecked(element, props); var value = getSafeValue(props.value); // 更新的重點在這里 if (value != null) { if (props.type === "number") { if (value === 0 && node.value === "" || // eslint-disable-next-line node.value != value) { node.value = "" + value; } } else if (node.value !== "" + value) { node.value = "" + value; } } if (props.hasOwnProperty("value")) { setDefaultValue(node, props.type, value); } else if (props.hasOwnProperty("defaultValue")) { setDefaultValue(node, props.type, getSafeValue(props.defaultValue)); } if (props.checked == null && props.defaultChecked != null) { node.defaultChecked = !!props.defaultChecked; } }
后面具體更新的細節就不分析了,相信大家都看的懂(可以忽略一些不重要的函數)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/97688.html
摘要:本文的目的是希望能針對這個問題提供一些說明現在暫時性的解決方案。完全不會有問題的只有一個瀏覽器,就是上面注釋中所說的已經實作出的,上可能根本不需要任何解決方案,它的輸入法編輯器是獨立于瀏覽器上的文本輸入框之外的。 問題來源是來自這個React官方存儲庫的issue #3926,與這個議題關聯的有很多其他的issue,來自許多項目,有些是與React相關,有些則是vue或其它JS套件。也...
摘要:假如我們從后臺拉取一個數據要填入輸入框,那么必須得使用受控組件,因為非受控組件只能被用戶輸入。不影響正常輸入填充該輸入框的默認值,此時不顯示內容。 網頁中使用的form表單大家肯定都再熟悉不過了,它主要作用是用來收集和提交信息。React中的表單組件與我們普通的Html中的表單及其表現形式沒有什么不同,所以如何使用表單我覺得再拿出來說可能是畫蛇添足、毫無意義。不過再怎么樣也不能辜負大家...
摘要:受控組件其值由控制的輸入表單元素稱為受控組件。如果讓表單數據由處理時,替代方案為使用非受控組件。使用非受控組件時,通常你希望可以為其指定初始值,但不再控制后續更新。 受控組件 其值由React控制的輸入表單元素稱為受控組件。 class NameForm extends React.Component { constructor(props) { super(props);...
摘要:具體表現是什么樣的呢圖安卓下不期望的輸入行為可以看到,在安卓手機下每次格式化發生的時候,本來應該一直在最后的光標會錯格一位,導致連續輸入出現問題。 今天要來說的是有關于有數值格式化的場景中,React input 光標的一些異常的表現和對應的處理辦法。故事要從一個 issue 說起,有用戶反映在使用 NumberField 組件輸入時安卓下會出現光標位置異常,導致連續輸入會達不到期望的...
閱讀 1772·2021-10-11 10:59
閱讀 2415·2021-09-30 09:53
閱讀 1776·2021-09-22 15:28
閱讀 2804·2019-08-29 15:29
閱讀 1567·2019-08-29 13:53
閱讀 3214·2019-08-29 12:34
閱讀 2864·2019-08-26 10:16
閱讀 2672·2019-08-23 15:16