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
- 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
- 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
- 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
- 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