Reuse is the foundation of componentized development system. The original intention of componentization is to reuse. However, component-based reuse also has some problems, and separation granularity is one of them. Today we will talk about cloneElement, a less commonly used API in React, and how it can help us better split components.
If we have a Layout components, so generally speaking this component mainly receive is children, put it on the main contents of the part, and nodes to control the Layout of the component itself, so this time if we have the Layout contains two parts, such as a header section, as the main content has obvious distinguish.
Such as:
So how do we design this component at this point?
Version of a
function Layout({ header: Header, children }) {
return (
<div className='container'>
<div className='header'>
<Header />
</div>
<div classNmae='content'>{children}</div>
</div>)}Copy the code
This is probably the most common way we pass in a specific component as Layout props, and then write it into the component render as it should be written.
If we want to use this component, it will look something like this:
function Header() {
return <h1>Title Here</h1>};<Layout header={Header}>
<div>content here</div>
</Layout>
Copy the code
So what’s the problem with that? Obviously there is, most notably the inability to specify props when using headers
If the Header has props, then we can only hardcode it in Layout and cannot declare it where the Header component is used. So if we want to reuse a Header component, we may need to declare another component. For example, we give the Header component a prop called Message that specifies the text content to display
function Header({ message = 'Title Here' }) {
return <h1>{message}</h1>
}
Copy the code
So if we want to reuse this component on different pages and display different titles, we need to do this:
function BigHeader() {
return <Header message='The Other Title' />
}
Copy the code
Obviously, this method can achieve certain reuse effect in the case of complex components and multiple props, but we certainly do not want to be limited to this in pursuit of perfection.
The second edition
Is there a way that we can specify props when we use it? The answer is yes, we can accept the Layout header prop as a concrete ReactElement instead of the component body.
function Layout({ header, children }) {
return (
<div className='container'>
<div className='header'>{header}</div>
<div classNmae='content'>{children}</div>
</div>)}Copy the code
So we can easily specify props when we are using it
<Layout header={<Header message='The Other Title' />}>
<div>Content Here</div>
</Layout>
Copy the code
To understand that we can do this, first we need to figure out what a ReactElement is. Since most of our React components are written using JSX, many of you may not know that ReactElement exists.
JSX is translated by Babel into the following code:
// jsx
;<div id='id'>content</div>
// js
React.createElement('div', { id: 'id' }, 'content')
Copy the code
This function takes three arguments
component
Specific rendered components, including native DOM nodes (string
) and custom components (object
)config
, including allprops
Coupled with thekey
andref
The dictionary object formedchildren
, the content of the child node, can beReactElement
,Array
,string
The content such as
Finally, it returns an object of type called ReactElement, which contains all the information contained in a node that will be used in the React rendering process. Our props. Children is a typical ReactElement.
So in the appeal example, the header we pass in is a ReactElement, so it can be used directly as a child of another node.
A great advantage of this approach is that you can even redefine a component and use Layout directly.
<Layout header={<h1>The Other Title</h1>}>
<div>Content Here</div>
</Layout>
Copy the code
And that can work, too.
So are we done here? NO, NO, NO. We still have some things to improve.
The third edition
Imagine if our Layout received the header as a node, but wanted to enforce some props on the component that was passed in? For example, if our Header component has another prop called Color, it specifies the display color of text content:
function Header({ message = 'Title Here', color = 'red' }) {
return <h1 style={{ color}} >{message}</h1>
}
Copy the code
We can also specify the prop when using the Header component, but if we need to specify a lot of prop and use Layout in a lot of places, we will obviously write a lot of duplicate code. In addition, if we need to modify this requirement later, it will lead to multiple modifications, and even some places forget to modify, resulting in bugs. So what do we do?
We can use an API that we don’t use very often, but which is useful in this scenario. This is react. cloneElement. Let’s modify the Layout
function Layout({ header, children }) {
return (
<div className='container'>
<div className='header'>
{React.cloneElement(header, { color: 'green' })}
</div>
<div classNmae='content'>{children}</div>
</div>)}Copy the code
By doing this, we actually render the Header and its props. Color will always be green. So what does this API mean?
As the name implies, it is used to clone a ReactElement that takes three parameters, the first being the target Element, the second being props, and the third being children. This is very similar to createElement, except that the first parameter is changed from a component to a node.
What he does is create a new ReactElement by copying the target Element and overriding the props of the original element with the following two parameters.
So at this point, our optimization process is pretty much the same. Of course, demo is obviously very simple code, but in real life, the problem is much more complicated, such as how to handle strings if you receive not a ReactElement but an array. So these questions will not continue in depth here, I leave you to think about it, after all, all changes are inseparable, after knowing the core idea, other problems can be easily solved.