react源碼解析-2Children

React.Children提供了對this.props.children的操作的函數。

react/src/ReactChildren文件export的函數如下

export {n forEachChildren as forEach,n mapChildren as map,n countChildren as count,n onlyChild as only,n toArray,n};n

forEachChildren

function forEachChildren(children, forEachFunc, forEachContext) {n if (children == null) {n return children;n }n var traverseContext = getPooledTraverseContext(n null,n null,n forEachFunc,n forEachContext,n );n traverseAllChildren(children, forEachSingleChild, traverseContext);n releaseTraverseContext(traverseContext);n}n

traverseContext在一個池中獲取了一個遍歷的上下文對象,這裡只是把forEachFunc和forEachContext封裝到一個對象里。

traverseAllChildren中調用了traverseAllChildrenImpl。

function traverseAllChildrenImpl(n children,n nameSoFar,n callback,n traverseContext,n) {n ...n}n

traverseAllChildrenImpl的參數children如果是數組,會遍歷children,會把每一個child遞歸調用自身。

參數children是一個單獨的react element或portal(16新增類型)時會調用callback

callback(n traverseContext,n children,n // If its the only child, treat the name as if it was wrapped in an arrayn // so that its consistent if the number of children grows.n nameSoFar === ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,n);n

forEachChildren的回調函數是forEachSingleChild,它的內容如下,這裡重新生成的key值沒有用到

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

它首先在bookKeeping(遍歷上下文)里,拿到我們在forEachChildren傳入的遍歷函數和上下文,用call調用一下,我們在下面就可以拿到child和index了。

React.Children.forEach(childrenArr,(child,index)=>{n ...n});n

最後釋放上下文對象

mapChildren

function mapChildren(children, func, context) {n if (children == null) {n return children;n }n var result = [];n mapIntoWithKeyPrefixInternal(children, result, null, func, context);n return result;n}n

mapIntoWithKeyPrefixInternal的內容如下,和forEachChildren基本一致,增加了存放返回值的數組和escapedPrefix=『』。

function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {n var escapedPrefix = ;n if (prefix != null) {n escapedPrefix = escapeUserProvidedKey(prefix) + /;n }n var traverseContext = getPooledTraverseContext(n array,n escapedPrefix,n func,n context,n );n traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);n releaseTraverseContext(traverseContext);n}n

不同之處在於traverseAllChildren的callback是mapSingleChildIntoContext。

function mapSingleChildIntoContext(bookKeeping, child, childKey) {n var {result, keyPrefix, func, context} = bookKeeping;nn var mappedChild = func.call(context, child, bookKeeping.count++);n if (Array.isArray(mappedChild)) {n mapIntoWithKeyPrefixInternal(n mappedChild,n result,n childKey,n emptyFunction.thatReturnsArgument,n );n } else if (mappedChild != null) {n if (isValidElement(mappedChild)) {n mappedChild = cloneAndReplaceKey(n mappedChild,n // Keep both the (mapped) and old keys if they differ, just asn // traverseAllChildren used to do for objects as childrenn keyPrefix +n (mappedChild.key && (!child || child.key !== mappedChild.key)n ? escapeUserProvidedKey(mappedChild.key) + /n : ) +n childKey,n );n }n result.push(mappedChild);n }n}n

var mappedChild = func.call(context, child, bookKeeping.count++)後如果是數組,再將結果數組通過mapIntoWithKeyPrefixInternal遞歸一遍。(之後的key值會加上.$)

得到單獨的元素時,將結果clone並且重設一遍key值,最後將結果push進result。

countChildren

function countChildren(children, context) {n return traverseAllChildren(children, emptyFunction.thatReturnsNull, null);n}n

traverseAllChildren,traverseAllChildrenImpl時有計數,將結果返回就好了。

onlyChild

function onlyChild(children) {n invariant(n isValidElement(children),n React.Children.only expected to receive a single React element child.,n );n return children;n}n

通過isValidElement判斷傳入的參數是否是「一個」react元素,否則就報錯。

export function isValidElement(object) {n return (n typeof object === object &&n object !== null &&n object.$$typeof === REACT_ELEMENT_TYPEn );n}n

toArray

function toArray(children) {n var result = [];n mapIntoWithKeyPrefixInternal(n children,n result,n null,n emptyFunction.thatReturnsArgument,n );n return result;n}n

和map類似,返回一個重設過key的元素的數組。

推薦閱讀:

vue-router源碼分析-整體流程
Vue.js起手式+Vue小作品實戰
如何解讀Facebook的這款疑似Virtual DOM專利?
遺世獨立的組件——Angular應用中的單組件構建
Phantom.js維護者Slobodin退出,項目未來將何去何從?

TAG:React | 前端框架 | 源码阅读 |