Explanation of React. cloneElement
- 2021-11-01 23:22:52
- OfStack
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
TimelineItem
0
Event, the user himself may define an independent for each TabPane
TimelineItem
0
Event, at which time we will modify the subcomponent
TimelineItem
0
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!