ReactNativeソースコードリーディング <View>

## View

画面を表示する機能
https://github.com/facebook/react-native/tree/master/Libraries/Components/View

## Reading

基本的にES5かつReact0.14以前のコードスタイルですね。

```js:View
const View = React.createClass({
  mixins: [NativeMethodsMixin],

  viewConfig: {
    uiViewClassName: 'RCTView',
    validAttributes: ReactNativeViewAttributes.RCTView
  },

  statics: {
    ...statics,
  },

  ...,
});
```

property定義。
次に一番重要であろうpropTypesを見ていきます。

```js:propTypes
  propTypes: {
    accessible: PropTypes.bool,

    accessibilityLabel: PropTypes.string,

    accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentType),
    /*
     * const AccessibilityComponentType = [
     *   'none',
     *   'button',
     *   'radiobutton_checked',
     *   'radiobutton_unchecked',
     * ];
     */

    accessibilityLiveRegion: PropTypes.oneOf([
      'none',
      'polite',
      'assertive',
    ]),

    // @platform android
    importantForAccessibility: PropTypes.oneOf([
      'auto',
      'yes',
      'no',
      'no-hide-descendants',
    ]),

    /*
     * - `'none'` - The element has no traits.
     * - `'button'` - The element should be treated as a button.
     * - `'link'` - The element should be treated as a link.
     * - `'header'` - The element is a header that divides content into sections.
     * - `'search'` - The element should be treated as a search field.
     * - `'image'` - The element should be treated as an image.
     * - `'selected'` - The element is selected.
     * - `'plays'` - The element plays sound.
     * - `'key'` - The element should be treated like a keyboard key.
     * - `'text'` - The element should be treated as text.
     * - `'summary'` - The element provides app summary information.
     * - `'disabled'` - The element is disabled.
     * - `'frequentUpdates'` - The element frequently changes its value.
     * - `'startsMedia'` - The element starts a media session.
     * - `'adjustable'` - The element allows adjustment over a range of values.
     * - `'allowsDirectInteraction'` - The element allows direct touch interaction for VoiceOver users.
     * - `'pageTurn'` - Informs VoiceOver that it should scroll to the next page when it finishes reading the contents of the element.
     *
     * @platform ios
     */
    accessibilityTraits: PropTypes.oneOfType([
      PropTypes.oneOf(AccessibilityTraits),
      PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)),
    ]),

    onAccessibilityTap: PropTypes.func,

    onMagicTap: PropTypes.func,

    testID: PropTypes.string,

    /*
     * touch interactions
     * func: (nativeEvent) => {}
     *
     * - `nativeEvent`
     *   - `changedTouches` - Array of all touch events that have changed since the last event.
     *   - `identifier` - The ID of the touch.
     *   - `locationX` - The X position of the touch, relative to the element.
     *   - `locationY` - The Y position of the touch, relative to the element.
     *   - `pageX` - The X position of the touch, relative to the root element.
     *   - `pageY` - The Y position of the touch, relative to the root element.
     *   - `target` - The node id of the element receiving the touch event.
     *   - `timestamp` - A time identifier for the touch, useful for velocity calculation.
     *   - `touches` - Array of all current touches on the screen.
     */

    // The View is now responding for touch events
    // reponderがactiveになる時に発火
    onResponderGrant: PropTypes.func,

    // The user is moving their finger.
    // ユーザの指が動いた時に発火
    onResponderMove: PropTypes.func,

    // Another responder is already active and will not release it to that `View` asking to be the responder.
    // 他のresponderがactiveで、responderがreleaseされずにこのresponderが発火できない時に発火
    onResponderReject: PropTypes.func,

    //　Fired at the end of the touch.
    // responderがreleaseされる時に発火
    onResponderRelease: PropTypes.func,

    // The responder has been taken from the `View`
    // responderが終了された時に発火
    onResponderTerminate: PropTypes.func,

    // Some other `View` wants to become responder and is asking this `View` to release its responder. 
    // Returning `true` allows its release.
    // 他のViewがresponderを扱いたいというリクエストを受信した時に発火
    // trueを返せばresponderをrelease
    onResponderTerminationRequest: PropTypes.func,

    // Does this view want to become responder on the start of a touch?
    // touch開始時にresponderになるかどうか、trueかfalseを返す必要あり
    onStartShouldSetResponder: PropTypes.func,

    // If a parent `View` wants to prevent a child `View` from becoming responder on a touch start,
    // it should have this handler which returns `true`.
    // childrenのViewがtouch開始時にresponderを扱うことを防ぐ場合はtrueを返す
    onStartShouldSetResponderCapture: PropTypes.func,

    // Does this view want to "claim" touch responsiveness?
    // touchに主張した時はtrueを返す
    onMoveShouldSetResponder: PropTypes.func,

    // If a parent `View` wants to prevent a child `View` from becoming responder on a move,
    // it should have this handler which returns `true`.
    // childrenのViewがmove時にresponderを扱うことを防ぐ場合はtrueを返す
    onMoveShouldSetResponderCapture: PropTypes.func,

    /**
     * This defines how far a touch event can start away from the view.
     * Typical interface guidelines recommend touch targets that are at least
     * 30 - 40 points/density-independent pixels.
     *
     * For example, if a touchable view has a height of 20 the touchable height can be extended to
     * 40 with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`
     *
     * touchできる範囲を指定する
     */
    hitSlop: EdgeInsetsPropType,

    /**
     * Invoked on mount and layout changes with:
     *
     * `{nativeEvent: { layout: {x, y, width, height}}}`
     *
     * This event is fired immediately once the layout has been calculated, but
     * the new layout may not yet be reflected on the screen at the time the
     * event is received, especially if a layout animation is in progress.
     *
     * layoutが変化した時に一度呼ばれる
     */
    onLayout: PropTypes.func,

    // touch eventのtagetになるかを指定する
    pointerEvents: PropTypes.oneOf([
      'box-none',
      'none',
      'box-only',
      'auto',
    ]),

    style: stylePropType,

    // subviewをたくさん持っている場合に不必要に表示しないようにする？
    removeClippedSubviews: PropTypes.bool,

    // Whether this `View` should render itself (and all of its children) into a
    // single hardware texture on the GPU.
    // @platform android
    renderToHardwareTextureAndroid: PropTypes.bool,

    // Whether this `View` should be rendered as a bitmap before compositing.
    // @platform ios
    shouldRasterizeIOS: PropTypes.bool,

    // childrenを描画するためだけの場合に表示を最適化する
    // @platform android
    collapsable: PropTypes.bool,

    // @platform android
    needsOffscreenAlphaCompositing: PropTypes.bool,
  },
```

主にtouch eventを制御するpropsですね。
最後にrenderを見てみます。

```js:render
  render: function() {
    // WARNING: This method will not be used in production mode as in that mode we
    // replace wrapper component View with generated native wrapper RCTView. Avoid
    // adding functionality this component that you'd want to be available in both
    // dev and prod modes.
    return <RCTView {...this.props} />;
  },
```

何やらproductionではこのView自体がrenderingされるわけではないらしい・・・？
もう少し後の処理を見てみます。

```js:View.js
const RCTView = requireNativeComponent('RCTView', View, {
  nativeOnly: {
    nativeBackgroundAndroid: true,
  }
});

let ViewToExport = RCTView;
if (__DEV__) {
  ViewToExport = View;
} else {
  Object.assign(RCTView, statics);
}

module.exports = ViewToExport;
```

ここですね。`__DEV__=true`の場合にView Componentがexportされて、productionではRCTViewがexportされているようです。
productionを見ないと意味がないので、RCTViewを生成している`requireNativeComponent`を見ていきます。

https://github.com/facebook/react-native/blob/master/Libraries/ReactIOS/requireNativeComponent.js

```js:requireNativeComponent.js
function requireNativeComponent(
  viewName: string,
  componentInterface?: ?ComponentInterface,
  extraConfig?: ?{nativeOnly?: Object},
): Function {
  var viewConfig = UIManager[viewName];
  if (!viewConfig || !viewConfig.NativeProps) {
    warning(false, 'Native component for "%s" does not exist', viewName);
    return UnimplementedView;
  }
  var nativeProps = {
    ...UIManager.RCTView.NativeProps,
    ...viewConfig.NativeProps,
  };
  viewConfig.uiViewClassName = viewName;
  viewConfig.validAttributes = {};
  viewConfig.propTypes = componentInterface && componentInterface.propTypes;
  for (var key in nativeProps) {
    var useAttribute = false;
    var attribute = {};

    var differ = TypeToDifferMap[nativeProps[key]];
    if (differ) {
      attribute.diff = differ;
      useAttribute = true;
    }

    var processor = TypeToProcessorMap[nativeProps[key]];
    if (processor) {
      attribute.process = processor;
      useAttribute = true;
    }

    viewConfig.validAttributes[key] = useAttribute ? attribute : true;
  }

  viewConfig.validAttributes.style = ReactNativeStyleAttributes;

  return createReactNativeComponentClass(viewConfig);
}
```

`createReactNativeComponentClass`を返しているので見てみます。
https://github.com/facebook/react/blob/master/src/renderers/native/createReactNativeComponentClass.js
(なぜかReact本体の方にある・・・)

```js:createReactNativeComponentClass
type ReactNativeBaseComponentViewConfig = {
  validAttributes: Object;
  uiViewClassName: string;
  propTypes?: Object,
}

var createReactNativeComponentClass = function(
  viewConfig: ReactNativeBaseComponentViewConfig
): ReactClass<any> {
  var Constructor = function(element) {
    this._currentElement = element;
    this._topLevelWrapper = null;
    this._hostParent = null;
    this._hostContainerInfo = null;
    this._rootNodeID = null;
    this._renderedChildren = null;
  };
  Constructor.displayName = viewConfig.uiViewClassName;
  Constructor.viewConfig = viewConfig;
  Constructor.propTypes = viewConfig.propTypes;
  Constructor.prototype = new ReactNativeBaseComponent(viewConfig);
  Constructor.prototype.constructor = Constructor;

  return ((Constructor: any): ReactClass<any>);
};
```

ReactClassのprototype chainしたConstructorが返されるようですね。
prototypeの`ReactNativeBaseComponent`を見ていきましょう。

https://github.com/facebook/react/blob/master/src/renderers/native/ReactNativeBaseComponent.js

```js:ReactNativeBaseComponent
type ReactNativeBaseComponentViewConfig = {
  validAttributes: Object;
  uiViewClassName: string;
}

var ReactNativeBaseComponent = function(
  viewConfig: ReactNativeBaseComponentViewConfig
) {
  this.viewConfig = viewConfig;
};

Object.assign(
  ReactNativeBaseComponent.prototype,
  ReactMultiChild.Mixin,
  ReactNativeBaseComponent.Mixin,
  NativeMethodsMixin
);
```

色々なmethodがmixinされているようです。

これでproductionでは、`ReactNativeBaseComponent`の各methodとViewのpropsを持つReact.Componentが出来上がることがわかりました。

次回は何かしらのtouch eventの仕組みを見ていこうと思います。