LoginSignup
3
2

More than 5 years have passed since last update.

Reactのstateはどのように推移するのか その4

Posted at

見落としていた点

どうやらhtml要素をmountする段階でpropertyをupdateする時にtransactionをqueueに詰めているのそこを見てみる。

ReactDOMComponent.js
  mountComponent: function(
    transaction,
    nativeParent,
    nativeContainerInfo,
    context
  ) {
    ...,

    this._updateDOMProperties(null, props, transaction);
  },

  _updateDOMProperties: function(lastProps, nextProps, transaction) {
    ...,

    for (propKey in nextProps) {
      ...,
      if ... {
        ...,
      } else if (registrationNameModules.hasOwnProperty(propKey)) {
        if (nextProp) {
          enqueuePutListener(this, propKey, nextProp, transaction);
        } else if (lastProp) {
          deleteListener(this, propKey)
      } ...,
    }
  },
var registrationNameModules = EventPluginRegistry.registrationNameModules;
EventPluginRegistry.js
var EventPluginRegistry = {
  ...,

  registrationNameModules: {},
};

空だが、実はinjectの最中に登録されていた。

function recomputePluginOrdering() {
  ...,

  for (var pluginName in namesToPlugins) {
    var PluginModule = namesToPlugins[pluginName];
    ...,
    EventPluginRegistry.plugins[pluginIndex] = PluginModule;
    var publishedEvents = PluginModule.eventTypes;
    for (var eventName in publishedEvents) {
      invariant(
        publishEventForPlugin(
          publishedEvents[eventName],
          PluginModule,
          eventName
        ),
        'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
        eventName,
        pluginName
      );
    }
  }
}

function publishEventForPlugin(dispatchConfig, PluginModule, eventName) {
  ...,
  EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig;
  var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
  if (phasedRegistrationNames) {
    for (var phaseName in phasedRegistrationNames) {
      if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
        var phasedRegistrationName = phasedRegistrationNames[phaseName];
        publishRegistrationName(
          phasedRegistrationName,
          PluginModule,
          eventName
        );
      }
    }
    return true;
  } else if (dispatchConfig.registrationName) {
    publishRegistrationName(
      dispatchConfig.registrationName,
      PluginModule,
      eventName
    );
    return true;
  }
  return false;
}

function publishRegistrationName(registrationName, PluginModule, eventName) {
  ...,
  EventPluginRegistry.registrationNameModules[registrationName] = PluginModule;
  EventPluginRegistry.registrationNameDependencies[registrationName] =
    PluginModule.eventTypes[eventName].dependencies;
  ...,
}
namesToPlugins
namesToPlugins = {
  SimpleEventPlugin: SimpleEventPlugin,
  EnterLeaveEventPlugin: EnterLeaveEventPlugin,
  ChangeEventPlugin: ChangeEventPlugin,
  SelectEventPlugin: SelectEventPlugin,
  BeforeInputEventPlugin: BeforeInputEventPlugin,
}
SimpleEventPlugin
var eventTypes = {
  ...,
  click: {
    phasedRegistrationNames: {
      bubbled: keyOf({onClick: true}),
      captured: keyOf({onClickCapture: true}),
    },
  },
  ...,
};

var SimpleEventPlugin = {
  eventTypes: eventTypes,
  ...,
};
keyOf
var keyOf = function(oneKeyObj) {
  var key;
  for (key in oneKeyObj) {
    if (!oneKeyObj.hasOwnProperty(key)) {
      continue;
    }
    return key;
  }
  return null;
};

keyだけを取り出すfunciton。
ここまでで、registrationNameModules['onClick']=SimpleEventPluginとなる。
あとはonChangeなどの基本的なイベントがPluginによって提供されている。
EventHandler用の分岐処理ということがわかった。
よって、nametextなどのpropertyは最後の分岐を通る。

ReactDOMComponent.js
function isCustomComponent(tagName, props) {
  return tagName.indexOf('-') >= 0 || props.is != null;
}

  ...,
  _updateDOMProperties: function(lastProps, nextProps, transaction) {
    ...,

    for (propKey in nextProps) {
      ...,
      if ... {
        ...,
      } else if ..., {
        ...,
      } else if (isCustomComponent(this._tag, nextProps)) {
        ...,
      } else if ..., {
        ...,
      }
    }
  },

-をtagが含んでいればCusotmComponentと判断されるようだ。

ReactDOMComponent.js
  _updateDOMProperties: function(lastProps, nextProps, transaction) {
    ...,

    for (propKey in nextProps) {
      ...,
      if ... {
        ...,
      } else if ..., {
        ...,
      } else if ..., {
        ...,
      } else if 
          DOMProperty.properties[propKey] ||
          DOMProperty.isCustomAttribute(propKey)) {
        var node = getNode(this);
        // If we're updating to null or undefined, we should remove the property
        // from the DOM node instead of inadvertently setting to a string. This
        // brings us in line with the same behavior we have on initial render.
        if (nextProp != null) {
          DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
        } else {
          DOMPropertyOperations.deleteValueForProperty(node, propKey);
        }
      }
    }
  },
DOMProperty.properties = HTMLDOMPropertyConfig.Properties = {
  // 色々ある
}

DOMProperty.properties = SVGDOMPropertyConfig.Properties = {
  // 色々ある
  // attributeに実際のpropertyは含まれている
  // property自体はすべて0が詰められている
}
DOMPropertyOperations.js
var DOMPropertyOperations = {
  ...,
  setValueForProperty: function(node, name, value) {
    ...,
    var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
        DOMProperty.properties[name] : null;
    if (propertyInfo) {
      var mutationMethod = propertyInfo.mutationMethod;
      if (mutationMethod) {
        mutationMethod(node, value);
      } else if (shouldIgnoreValue(propertyInfo, value)) {
        this.deleteValueForProperty(node, name);
      } else if (propertyInfo.mustUseProperty) {
        var propName = propertyInfo.propertyName;
        // Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
        // property type before comparing; only `value` does and is string.
        if (!propertyInfo.hasSideEffects ||
            ('' + node[propName]) !== ('' + value)) {
          // Contrary to `setAttribute`, object properties are properly
          // `toString`ed by IE8/9.
          node[propName] = value;
        }
      } else {
        ...,
      }
    } else if ... {
      ...,
    }
  },
  ...,
};
DOMProperty.js
var DOMPropertyInjection = {
      ...,
      var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
      ...,
      if (DOMMutationMethods.hasOwnProperty(propName)) {
        propertyInfo.mutationMethod = DOMMutationMethods[propName];
      }
      ...,
};

DOMMutationMethodsはnullなので無視。

node.setAttribute(attributeName, '' + value);

普通のpropertyの場合はこちら。

node = getNode(ReactDOMComponent) = ReactDOMComponentTree.getNodeFromInstance(ReactDOMComponent)
ReactDOMComponentTree.js
function getInstanceFromNode(node) {
  var inst = getClosestInstanceFromNode(node);
  if (inst != null && inst._nativeNode === node) {
    return inst;
  } else {
    return null;
  }
}

var ReactDOMComponentTree = {
  ...,
  getNodeFromInstance: getNodeFromInstance,
  ...,
};
ReactDOMComponentTree.js
function getClosestInstanceFromNode(node) {
  if (node[internalInstanceKey]) {
    return node[internalInstanceKey];
  }

  // Walk up the tree until we find an ancestor whose instance we have cached.
  var parents = [];
  while (!node[internalInstanceKey]) {
    parents.push(node);
    if (node.parentNode) {
      node = node.parentNode;
    } else {
      // Top of the tree. This node must not be part of a React tree (or is
      // unmounted, potentially).
      return null;
    }
  }

  var closest;
  var inst;
  for (; node && (inst = node[internalInstanceKey]); node = parents.pop()) {
    closest = inst;
    if (parents.length) {
      precacheChildNodes(inst, node);
    }
  }

  return closest;
}

internalInstance(wrapされたComponent)を持つ親があればそれを返す。
なければ持つ親を探して、それをキャッシュして返すだけ。
最終的にはnodeのpropsに値を詰めているだけのように思える。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2