preface

The React. CloneElement method comes up from time to time, and I still don’t know how to use it, so dig deep into the source code layer to find a way to call it. This article will summarize the use of the React. CloneElement from the source layer

version

  • React source code version: 16.6.1

The body of the

usage

  • Clone the React element and return the React element. You can modify props, key, and ref using the config parameter. You can modify children using the React element
  • Any number of arguments can be passed to the React. CloneElement, starting with the third argument, will be children of the new React element
  • The sample
import React from "react";
import "./style.css";

export default function App() {
  const Clone = React.cloneElement(<Temp/>, {key: 123.name: "Zhang"}, <div>Hello World 1</div>.<div>Hello World 2</div>)
  return (
    <div>
      {Clone}
    </div>
  );
}

const Temp = (props) = > {
  return (
    <div>
        <span>Hello world, {props. Name}</span>
        {props.children}
    </div>)};// Page outputHello world, John Hello world1HELLO WORLD2
Copy the code

Parameter analysis

element

The elements of the react

config

Object parameter, which can contain the following properties

  • Ref: Optional, replace the ref in the original element
  • Key: Optional, replaces the key in the original element
  • Other properties: All as props for the new Element
children

Child element parameters, which would be more appropriate… children

parsing

  1. Declare props, key, ref, self, source, and owner with default values based on element
  const props = Object.assign({}, element.props);
  // Reserved names are extracted
  let key = element.key;
  let ref = element.ref;
  // Self is preserved since the owner is preserved.
  const self = element._self;
  // Source is preserved since cloneElement is unlikely to be targeted by a
  // transpiler, and the original source is probably a better indicator of the
  // true owner.
  const source = element._source;
  // Owner will be preserved, unless ref is overridden
  let owner = element._owner;
Copy the code
  1. In the attributes of config, look for key and ref and pass them separately to the new element, if any
if (hasValidRef(config)) {
    // Silently steal the ref from the parent.
    ref = config.ref;
    owner = ReactCurrentOwner.current;
 }
 if (hasValidKey(config)) {
    key = ' ' + config.key;
 }
Copy the code
  1. The config attribute is iterated, filtering key, ref, __self, __source, and attributes on the prototype chain. The remaining attributes are assigned to the props of the new element
for (propName in config) {
   if( hasOwnProperty.call(config, propName) && ! RESERVED_PROPS.hasOwnProperty(propName) ) {if (config[propName] === undefined&& defaultProps ! = =undefined) {
            // Resolve default props
            props[propName] = defaultProps[propName];
        } else{ props[propName] = config[propName]; }}}Copy the code
  1. Iterate over the remaining parameters as children of the new element
  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }
Copy the code

The flow chart

The source code

import invariant from 'shared/invariant';
import ReactCurrentOwner from './ReactCurrentOwner';
const hasOwnProperty = Object.prototype.hasOwnProperty;
const RESERVED_PROPS = {
  key: true.ref: true.__self: true.__source: true};/** * Clone and return a new ReactElement using element as the starting point. * See https://reactjs.org/docs/react-api.html#cloneelement */
export function cloneElement(element, config, children) { invariant( ! (element ===null || element === undefined),
    'React.cloneElement(...) : The argument must be a React element, but you passed %s.',
    element,
  );
  let propName;
  // Original props are copied
  const props = Object.assign({}, element.props);
  // Reserved names are extracted
  let key = element.key;
  let ref = element.ref;
  // Self is preserved since the owner is preserved.
  const self = element._self;
  // Source is preserved since cloneElement is unlikely to be targeted by a
  // transpiler, and the original source is probably a better indicator of the
  // true owner.
  const source = element._source;
  // Owner will be preserved, unless ref is overridden
  let owner = element._owner;
  if(config ! =null) {
    if (hasValidRef(config)) {
      // Silently steal the ref from the parent.
      ref = config.ref;
      owner = ReactCurrentOwner.current;
    }
    if (hasValidKey(config)) {
      key = ' ' + config.key;
    }
    // Remaining properties override existing props
    let defaultProps;
    if (element.type && element.type.defaultProps) {
      defaultProps = element.type.defaultProps;
    }
    for (propName in config) {
      if( hasOwnProperty.call(config, propName) && ! RESERVED_PROPS.hasOwnProperty(propName) ) {if (config[propName] === undefined&& defaultProps ! = =undefined) {
          // Resolve default props
          props[propName] = defaultProps[propName];
        } else{ props[propName] = config[propName]; }}}}// Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }
  return ReactElement(element.type, key, ref, self, source, owner, props);
}
Copy the code