React Routerは何をしているのか？~routes作成編~

## 見逃していた点
* componentsがRouter.propsにどのように設定されるのか
* 実は、transitionManager作成時に、childrenからroutesを作成していた！

```js:Router.js
const Router = React.createClass({
  ...,

  createRouterObjects() {
    ...,

    const transitionManager = createTransitionManager(
      history, createRoutes(routes || children)
    )
    
    ...,
  },
}
```

はい、ここですね。
createRoutesというmethodでroutesを作成しています・・・見えにくい！

```js:createRoutes
export function createRoutes(routes) {
  if (isReactChildren(routes)) {
    routes = createRoutesFromReactChildren(routes)
  } else if (routes && !Array.isArray(routes)) {
    routes = [ routes ]
  }

  return routes
}

function isValidChild(object) {
  return object == null || React.isValidElement(object)
}

export function isReactChildren(object) {
  return isValidChild(object) || (Array.isArray(object) && object.every(isValidChild))
}
```

大体がisReactChildrenを満たすので進みます。

```js:createRoutesFromReactChildren
export function createRoutesFromReactChildren(children, parentRoute) {
  const routes = []

  React.Children.forEach(children, function (element) {
    if (React.isValidElement(element)) {
      // Component classes may have a static create* method.
      if (element.type.createRouteFromReactElement) {
        const route = element.type.createRouteFromReactElement(element, parentRoute)

        if (route)
          routes.push(route)
      } else {
        routes.push(createRouteFromReactElement(element))
      }
    }
  })

  return routes
}
```

React.Children.forEachが何なのかを確認します。

```js:ReactIsomorphic.js
var React = {

  Children: {
    map: ReactChildren.map,
    forEach: ReactChildren.forEach,
    count: ReactChildren.count,
    toArray: ReactChildren.toArray,
    only: onlyChild,
  },

  ...,

};
```

```js:ReactChildren.js
var ReactChildren = {
  forEach: forEachChildren,
  map: mapChildren,
  mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,
  count: countChildren,
  toArray: toArray,
};

function forEachChildren(children, forEachFunc, forEachContext) {
  if (children == null) {
    return children;
  }
  var traverseContext =
    ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
  traverseAllChildren(children, forEachSingleChild, traverseContext);
  ForEachBookKeeping.release(traverseContext);
}
```

ForEachBookKeepingを見てみます。

```js:ForEachBookKeeping
function ForEachBookKeeping(forEachFunction, forEachContext) {
  this.func = forEachFunction;
  this.context = forEachContext;
  this.count = 0;
}
ForEachBookKeeping.prototype.destructor = function() {
  this.func = null;
  this.context = null;
  this.count = 0;
};
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
```

forEachSingleChildを見に行きます。

```js:forEachSingleChild
function forEachSingleChild(bookKeeping, child, name) {
  var {func, context} = bookKeeping;
  func.call(context, child, bookKeeping.count++);
}
```

contextがfuncを呼び出すだけ。

```js:traverseAllChildren
function traverseAllChildren(children, callback, traverseContext) {
  if (children == null) {
    return 0;
  }

  return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
```

```js:traverseAllChildrenImpl
function traverseAllChildrenImpl(
  children,
  nameSoFar,
  callback,
  traverseContext
) {
  var type = typeof children;

  if (type === 'undefined' || type === 'boolean') {
    // All of the above are perceived as null.
    children = null;
  }

  if (children === null ||
      type === 'string' ||
      type === 'number' ||
      ReactElement.isValidElement(children)) {
    callback(
      traverseContext,
      children,
      // If it's the only child, treat the name as if it was wrapped in an array
      // so that it's consistent if the number of children grows.
      nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
    );
    return 1;
  },

  ...,
}
```

このmethod自体は、描画されるchildrenの数を返します。
まず、childrenが1つのときはcallbackを実行してから1を返してます。

```js:traverseAllChildrenImple
つづき
  var child;
  var nextName;
  var subtreeCount = 0; // Count of children found in the current subtree.
  var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;

  if (Array.isArray(children)) {
    for (var i = 0; i < children.length; i++) {
      child = children[i];
      nextName = nextNamePrefix + getComponentKey(child, i);
      subtreeCount += traverseAllChildrenImpl(
        child,
        nextName,
        callback,
        traverseContext
      );
    }
  } else {
    ...,
  }

  return subtreeCount
}
```

`nextNamePrefix='.'`となっています。
childは子要素のReactElementとなります。

```js:getComponentKey
function getComponentKey(component, index) {
  // Do some typechecking here since we call this blindly. We want to ensure
  // that we don't block potential future ES APIs.
  if (component && typeof component === 'object' && component.key != null) {
    // Explicit key
    return wrapUserProvidedKey(component.key);
  }
  // Implicit key determined by the index in the set
  return index.toString(36);
}
```

keyは今回ないので、intを36進数で表したものになります。
forEachSingleChildrenでnameは使用されていないのであまり意味はなさそうですね。
あとは再帰的にcallbackを実行してchildrenの数をcountしていくだけのようです。
結果的にcallbackをchildrenに実行していくと考えて問題ないですね。

createRoutesFromReactChildrenに戻ります。

```js:createRoutsFromReactChildren
export function createRoutesFromReactChildren(children, parentRoute) {
  ...,

  React.Children.forEach(children, function (element) {
    if (React.isValidElement(element)) {
      // Component classes may have a static create* method.
      if (element.type.createRouteFromReactElement) {
        ...,
      } else {
        routes.push(createRouteFromReactElement(element))
      }
    }
  })

  return routes
}
```

ChildrenのReactComponentにcreateRouteFromReactElementのmethodが定義されていればそれを使用し、なければ用意されているものを使用するようです。
今回は定義していないと仮定して、用意されているものを見ていきます。

```js:createRouteFromReactElement
export function createRouteFromReactElement(element) {
  const type = element.type
  const route = createRoute(type.defaultProps, element.props)

  if (type.propTypes)
    checkPropTypes(type.displayName || type.name, type.propTypes, route)

  if (route.children) {
    const childRoutes = createRoutesFromReactChildren(route.children, route)

    if (childRoutes.length)
      route.childRoutes = childRoutes

    delete route.children
  }

  return route
}
```

ReactElementのpropsとReactComponentのdefaultProps(getDefaultPropsで定義したもの)を結合してrouteを作っています。
ReactComponentのpropTypesをcheckにかけたあと、ReactElementがchildrenをもっていたら、childrenに対してcreateRoutesFromReactChildrenを再帰的に実行していきます。
childRoutsがあればrouteにchildRoutesプロパティを設定。
設定したらrouteからchildrenを削除しています。

以上をReactElementすべてに実行すれば、routeの配列が出来上がり、終了となります。