React source code interpretation | React.cloneElement analysis

May 24, 2023 1346hotness 0likes 0comments

preface

The React.cloneElement method is encountered from time to time, and I read the official website documents many times, but I still have little knowledge of how to use it. Therefore, I go deep into the source code layer to find a calling solution. This article will analyze and summarize the usage of React.cloneElement from the source code layer

version

  • react source code version: 16.6.1

body

usage

  • Clone with element element as template and return new React element. You can modify props, key, ref through config parameter, and child element
  • through children.
    Any parameter can be passed in

  • React.cloneElement, and will be used as a child element of the new React element starting from the third parameter
  • example
import React from "react";
import "./style.css";

export default function App() {
  const Clone = React.cloneElement(<Temp/>, {key: 123, name: "Zhang San"}, <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 output
Hello world, Zhang San
Hello, World.1
Hello, World.2

Parameter analysis

element

elements of react

config

object parameter, which can contain the following properties

  • ref: not required to replace ref in the original element
  • key: not required to replace key in the original element
  • other attributes: all props as new element
children

The child element parameter is more appropriate here. Children

parse

  1. declare that props, key, ref, self, source, owner are assigned 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;
  1. in the attributes of config, look for key and ref, and if any, take them out separately and pass them to the new element
if (hasValidRef(config)) {
    // Silently steal the ref from the parent.
    ref = config.ref;
    owner = ReactCurrentOwner.current;
 }
 if (hasValidKey(config)) {
    key = '' + config.key;
 }
  1. traverses the config attribute, filtering the attributes on key, ref, _ _ self, _ _ source and the prototype chain, and assigning the remaining attributes to the props
  2. 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];
        }
   }
}
  1. traverses the remaining parameters as the children
  2. 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;
  }

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);
}
InterServer Web Hosting and VPS

Aaron

Hello, my name is Aaron and I am a freelance front-end developer

Comments