Explanation of React. cloneElement

  • 2021-11-01 23:22:52
  • OfStack

Function and usage scenarios of directory cloneElement
Add a new props
Events for modifying props
Custom style
Add key
Summarize

Because I want to take over and maintain some projects, the technical stack of the team recently changed from vue to react. As a novice of react, and I like to learn new things through source code, I chose to learn some usage of react by reading antd, a famous project source code.

In the process of reading the source code, I found that many components use React. cloneElement this api. Although I can guess what it did by name, I don't know the specific role; Then look at the official document, which clearly describes its function, but does not tell us what scenarios we need to use it. So I according to the description of the document, combined with the use of source code, facing google and stackoverflow, summed up a number of use scenarios.

Role of cloneElement


React.cloneElement(
 element,
 [props],
 [...children]
)

First, look at the description of this API in the official document:

Clone and return a new React element using element as the starting point. The resulting element will have the original element's props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved.

To sum up, it is:

Clone the original element and return a new React element; The props of the original element is retained, and a new props can be added at the same time, and the two are superficially merged; key and ref will be retained because they are also props themselves, so they can also be modified; According to the source code of react, we can define any number of sub-elements from the third parameter. If a new children is defined, the original children will be replaced;

Usage scenario

According to the above definition decomposition, we can use this api as needed in different scenarios.

Add a new props

When we create a common component, we want to add a different class name to each child element according to the internal logic. At this time, we can modify its className :

Suppose we have an Timeline component, which allows us to define multiple as needed TimelineItem Internally, we want to add one to the last TimelineItem timeline-item-last Class to render special effects. At this time, we can do this:


const MyTimeline = () => {
 return (
  <Timeline>
   <TimelineItem>2020-06-01</TimelineItem>
   <TimelineItem>2020-06-08</TimelineItem>
   <TimelineItem>2020-07-05</TimelineItem>
  </Timeline>
 )
}

//  In  Timeline  Internally, the logic might look like this 
import class from 'classnames';
const Timeline = props => {
 // ...
 // ...
 const itemCount = React.children.count(props.children);
 const items = React.children.map(props.children, (item, index) => {
  return React.cloneElement(item, {
   className: class([
    item.props.className,
    'timeline-item',
    index === count - 1 ? 'timeline-item-last' : ''
   ])
  })
 }
 return <div className={'timeline'}>{ items }</div>
}

In addition to adding className You can also dynamically add more props information to subcomponents, react-router Adj. Switch Adds to the matching subcomponent location And computedMatch Information:


class Switch extends React.Component {
 render() {
  return (
   <RouterContext.Consumer>
    {context => {
     invariant(context, "You should not use <Switch> outside a <Router>");

     const location = this.props.location || context.location;

     let element, match;

     // We use React.Children.forEach instead of React.Children.toArray().find()
     // here because toArray adds keys to all child elements and we do not want
     // to trigger an unmount/remount for two <Route>s that render the same
     // component at different URLs.
     React.Children.forEach(this.props.children, child => {
      if (match == null && React.isValidElement(child)) {
       element = child;

       const path = child.props.path || child.props.from;

       match = path
        ? matchPath(location.pathname, { ...child.props, path })
        : context.match;
      }
     });

     return match
      ? React.cloneElement(element, { location, computedMatch: match })
      : null;
    }}
   </RouterContext.Consumer>
  );
 }
}

Events for modifying props

Suppose we have an Tab component that contains multiple TabPane Subcomponent, we want to click on each TabPane Subcomponent triggers Tab at the same time TimelineItem0 Event, the user himself may define an independent for each TabPane TimelineItem0 Event, at which time we will modify the subcomponent TimelineItem0 Events:


const Tab = props => {
 const { onClick } = props;
 const tabPanes = React.children.map(props.children, (tabPane, index) => {
  const paneClick = () => {
   onClick && onClick(index);
   tabPane.props?.onClick();
  }
  return React.cloneElement(tabPane, {
    onClick: paneClick,
  })
 })
 return <div>{ tabPanes }</div>
}

Custom style

Create a file called FollowMouse Component, we allow users to define content components Content When the mouse moves, it automatically calculates according to the size of the content Content To avoid overflowing the screen, at this time we can use cloneElement To dynamically modify its style.


//  For simplicity, mouse events are omitted here. 
const FollowMouse = props => {
 const { Content } = props;
 const customContent = React.isValidElement ? Content : <span>{ Content }</span>
 const getOffset = () => {
  return {
   position: 'absolute',
   top: ...,
   left: ...,
  }
 }
 const renderContent = React.cloneElement(custonContent, {
  style: {
   ...getOffset()
  }
 })
 return <div>{ renderContent() }</div>
}

Add key

When we create a list of 1 elements, we can use the cloneElement Add 1 key to each node.


const ComponentButton = props => {
 const { addonAfter, children } = props;
 const button = <button key='button'>{ children }</button>
 const list = [button, addonAfter ? React.cloneElement(addonAfter, { key: 'button-addon' } : null)
 return <div>{ list } <div>
}

Summarize

In the development of complex components, it is often necessary to add different functions or display effects to subcomponents. react Element itself is an immutable (immutable) object, props.children In fact, it is not children In itself, it just children The descriptor (descriptor), we can't modify any of its attributes, we can only read the contents, so React.cloneElement Allows us to copy its elements and modify or add new props for our purposes.

Of course, thanks to the powerful combination mode of react, this is not limited to props.children , whether it is props.left Or props.right Or any other props Incoming content, as long as it is a legal react element, we can use this React.cloneElement Operate on it.

The above is the React. cloneElement use details, more information about the use of React. cloneElement please pay attention to other related articles on this site!


Related articles: