React source React.Children

children What does it mean? What we got props internal components when there props.children such a property, in most cases, we directly props.children rendered to the JSX inside it. Few cases we need to operate the children. But if you need to operate once the children. We recommend using React.children the api, rather than directly to operate him.

 

Although we get the children in most cases it is reasonable to react element or an array, but there React provide api to manipulate him, then he must have a reasonable reason.

 

React.js open source code, to find children
Children: {
    map,
    forEach,
    count,
    toArray,
    only,
}

This is a children object that there are five attributes, operations, attributes this 5 looks just like our array is very like the first two are the most important, is the map and forEach, equivalent to map an array of and forEach method, we can understand the meaning is the same, but the map and forEach actual operation of the array will be different with a little. Here is a map of all logic inside the most complex one. The map and forEach are similar, their only difference is that there is a return, a no return, after we pass through the map is a way to call out after a new array returned, and forEach return of the original array.

 

We look at a demo,

 

import React from 'react'

function ChildrenDemo(props) {
  console.log(props.children)
  console.log(React.Children.map(props.children, c => [c, [c, c]]))
  return props.children
}

export default () => (
  <ChildrenDemo>
    <span>1</span>
    <span>2</span>
  </ChildrenDemo>
)

We have created a component called ChildrenDemo, then wrapped inside a span as his two children, then in his props.children which you can get the first print is out of the props.children, we see that two an element node. Just before React Element property is the same. The second is through React.Children.map this api, then passed props.children. And pass a callback, the callback returns a nested array of two layers, may be more confused, you can look as c => [c, c], and then print out the children are 1,1,2, 2, if we can be understood as his eventual return is a unfold, because it is a total of two nodes, props.children two nodes, each node after returning by map function is an array, then the React.Children he started, then it becomes a one-dimensional array, the return would have been a two-dimensional array, then the array which has a two-dimensional array.

 

Here is nested several layers of matter, the return of one-dimensional array. This is an essential difference with an array of React.Children.map .map, the first parameter is an array of map sub-element, the first argument here is to traverse the array, and then return to a one-dimensional array. Next we look at how his source are doing. Open ReactChildren.js. Turn to the bottom, see

 

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

mapChildren as map, export out here map, in which this method is mapChildren

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

Find mapChildren this method, there is first determine whether the children equal null, if equal to null, then return directly. Then declare a result, this is the final return of the result. This time we could look forEachChildren

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

There's forEachChildren no result, no return value. This is an essential difference between them.



mapChildren which calls mapIntoWithKeyPrefixInternal, was introduced to children, result is we just declared, and the third is null, function that we pass the second argument, context is this.object, generally we do not have him, no matter what 
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
  let escapedPrefix = '';
  if (prefix != null) {
    escapedPrefix = escapeUserProvidedKey(prefix) + '/';
  }
  const traverseContext = getPooledTraverseContext(
    array,
    escapedPrefix,
    func,
    context,
  );
  traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
  releaseTraverseContext(traverseContext);
}

mapIntoWithKeyPrefixInternal which we first see him handle a bit key, the key to ignore, because he is a string handling related stuff, nothing special, and then call the following three functions under contrast with forEachChildren, is about the same. By calling getPooledTraverseContext, then went to get a traverseContext, this thing what is it? We look at this direct method 

 

const traverseContextPool = [];
function getPooledTraverseContext(
  mapResult,
  keyPrefix,
  mapFunction,
  mapContext,
) {
  if (traverseContextPool.length) {
    const traverseContext = traverseContextPool.pop();
    traverseContext.result = mapResult;
    traverseContext.keyPrefix = keyPrefix;
    traverseContext.func = mapFunction;
    traverseContext.context = mapContext;
    traverseContext.count = 0;
    return traverseContext;
  } else {
    return {
      result: mapResult,
      keyPrefix: keyPrefix,
      func: mapFunction,
      context: mapContext,
      count: 0,
    };
  }
}

We see this method is actually not any special treatment, he first determine what the global variable traverseContextPool, whether he is a node already exists, if any, from this traverseContextPool inside a pop, pop, and then passed to come mount directly to the top of him, did not do any other operation, in fact, used to record the object, and if not, return a new object, what significance does it have? Back and take a look, two subsequent method calls traverseContext, last sentence code releaseTraverseContext (traverseContext), releaseTraverseContext he is what does that mean?

 

function releaseTraverseContext(traverseContext) {
  traverseContext.result = null;
  traverseContext.keyPrefix = null;
  traverseContext.func = null;
  traverseContext.context = null;
  traverseContext.count = 0;
  if (traverseContextPool.length < POOL_SIZE) {
    traverseContextPool.push(traverseContext);
  }
}

We found that he is a target of the contents of this traverseContext gave emptied, and then determine whether traverseContextPool POOL_SIZE less than this maximum size limit is 10, if not greater than, fill push on the object. That it what is it? This is a very simple concept of an object pool, that is my map function is likely to be the method I used to be called, if he launched more layers, the object of my traverseContextPool statement will be more, if this more objects declared, every time I call the map functioin, must declare so many objects, then call over after he put so much object releases, this is actually a very consuming an operating performance since a declare objects, and a deleted object, then he is likely to cause memory problems jitter, and then let us see the overall performance within the browser page will be relatively poor, so here he set such a traverseContextPool. The total length and then give him a 10, followed by a gradual process, first he is an empty array, with the objects one by one to create, he would promote him to go, then took him out multiplexing, because we know js is a single-threaded when we perform a particular props.children, you can reuse the space. Then we continue to look traverseAllChildren 

 

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


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

In fact, this method is not anything special, then his return is actually a traverseAllChildrenImpl.

function traverseAllChildrenImpl(
  children,
  nameSoFar,
  callback,
  traverseContext,
) {
  const type = typeof children;


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


  let invokeCallback = false;


  if (children === null) {
    invokeCallback = true;
  } else {
    switch (type) {
      case 'string':
      case 'number':
        invokeCallback = true;
        break;
      case 'object':
        switch (children.$$typeof) {
          case REACT_ELEMENT_TYPE:
          case REACT_PORTAL_TYPE:
            invokeCallback = true;
        }
    }
  }


  if (invokeCallback) {
    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;
  }


  let child;
  let nextName;
  let subtreeCount = 0; // Count of children found in the current subtree.
  const nextNamePrefix =
    nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;


  if (Array.isArray(children)) {
    for (let i = 0; i < children.length; i++) {
      child = children[i];
      nextName = nextNamePrefix + getComponentKey(child, i);
      subtreeCount += traverseAllChildrenImpl(
        child,
        nextName,
        callback,
        traverseContext,
      );
    }
  } else {
    const iteratorFn = getIteratorFn(children);
    if (typeof iteratorFn === 'function') {
      if (__DEV__) {
        // Warn about using Maps as children
        if (iteratorFn === children.entries) {
          warning(
            didWarnAboutMaps,
            'Using Maps as children is unsupported and will likely yield ' +
              'unexpected results. Convert it to a sequence/iterable of keyed ' +
              'ReactElements instead.',
          );
          didWarnAboutMaps = true;
        }
      }


      const iterator = iteratorFn.call(children);
      let step;
      let ii = 0;
      while (!(step = iterator.next()).done) {
        child = step.value;
        nextName = nextNamePrefix + getComponentKey(child, ii++);
        subtreeCount += traverseAllChildrenImpl(
          child,
          nextName,
          callback,
          traverseContext,
        );
      }
    } else if (type === 'object') {
      let addendum = '';
      if (__DEV__) {
        addendum =
          ' If you meant to render a collection of children, use an array ' +
          'instead.' +
          ReactDebugCurrentFrame.getStackAddendum();
      }
      const childrenString = '' + children;
      invariant(
        false,
        'Objects are not valid as a React child (found: %s).%s',
        childrenString === '[object Object]'
          ? 'object with keys {' + Object.keys(children).join(', ') + '}'
          : childrenString,
        addendum,
      );
    }
  }


  return subtreeCount;
}

Then this approach is a focus, and if the children here is a single node, he is not an array, so this time he will enter the inside of this judgment, is undefined, string, number, object, and so on. object is a REACT_ELEMENT_TYPE or REACT_PORTAL_TYPE. They are reasonable react may render node, then call invokeCallback assignment is true, invokeCallback is true is that a direct call callback, callback here is passed in such a way mapSingleChildIntoContext, they all have one thing in common, that is, they can not traverse an array or object, so they are a single node, so for a single node, you can call the callback directly. If the array is how to do, if it is an array, he would go to loop through the array, and then he calls his own, then put the child into the pass, which is a recursive process until a single time, go call the callback, then the callback, what is it, mapSingleChildIntoContext

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
  const {result, keyPrefix, func, context} = bookKeeping;


  let mappedChild = func.call(context, child, bookKeeping.count++);
  if (Array.isArray(mappedChild)) {
    mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
  } else if (mappedChild != null) {
    if (isValidElement(mappedChild)) {
      mappedChild = cloneAndReplaceKey(
        mappedChild,
        // Keep both the (mapped) and old keys if they differ, just as
        // traverseAllChildren used to do for objects as children
        keyPrefix +
          (mappedChild.key && (!child || child.key !== mappedChild.key)
            ? escapeUserProvidedKey(mappedChild.key) + '/'
            : '') +
          childKey,
      );
    }
    result.push(mappedChild);
  }
}

This approach inside did it, passing three parameters, the first parameter is bookKeeping, is traverseContext, is the pool inside. The second parameter is traverseAllChildrenImpl child inside until the individual children, childKey is a mapping relationship, this can be ignored.

 

Then there called bookKeeping inside func, is traverseContext inside func, is that the callback function. Then the node pass, then pass a index, returned to mappedChild after the call, and then determine whether it is an array, if it is an array, calls mapIntoWithKeyPrefixInternal again, this is a big recursive, recursion this time is not the time call func, but his c => c, return directly to the node, otherwise an infinite loop, and for the map had chilren, direct return to the current node on it. In the end, it will determine whether it is reasonable under mappedChild element, that is isValidElement, after a judge will determine cloneAndReplaceKey,

 

export function cloneAndReplaceKey(oldElement, newKey) {
  const newElement = ReactElement(
    oldElement.type,
    newKey,
    oldElement.ref,
    oldElement._self,
    oldElement._source,
    oldElement._owner,
    oldElement.props,
  );


  return newElement;
}

Here cloneAndReplaceKey is also very simple, he is actually return a new react element. In addition to a newKey, others are returned on the basis of the original react element

 

This is the map function of two nested recursive process, all the recursive array, and then he launched a process to return, so here getPooledTraverseContext inside the pool, how many arrays, there are that many objects, this is the pool setting here the meaning of the case, he is not only the case of one layer, always only one object, if there are nested array, but nesting is more and more, his sense of relatively big. This is React.Children.map implementation process. forEachChildren similar with mapChildren. Here are a few, toArray



function toArray(children) {
  const result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, child => child);
  return result;
}

toArray relatively simple, but mapChildren inside layer.

 

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

onlyChild in fact, determine whether it is a single logical react element node.

 

Guess you like

Origin www.cnblogs.com/wzndkj/p/11963333.html