Reactソースを読む:Controlled Input


みんなが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]);
    }
  }
}

ここでは2つの共通変数:restoreTargetrestoreQueueに関連していますが、この2つの変数はどのような状況で変化しますか?
同じファイル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

方法
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;
  }
}

後で具体的に更新する細部は分析しないで、みんながすべて理解していることを信じます(いくつかの重要でない関数を無視することができます)