共计 3068 个字符,预计需要花费 8 分钟才能阅读完成。
复用是组件化开发体系的立命之本,能够说组件化的初衷就是为了复用性。然而组件化的复用形式也存在肯定的问题,其中拆分粒度就是其中一个绕不开的话题,明天咱们就来讲一讲 React 当中的一个不太罕用的 API:
cloneElement
,他如何帮组咱们更好得进行组件拆分。
如果咱们有一个 Layout
组件,那么一般来说这个组件次要接管的就是 children
,把它放在次要内容的局部,而后组件自身的节点来管制布局,那么这个时候如果咱们这个布局蕴含两个局部呢,比方还有一个header
局部,是跟次要内容有显著辨别的。
比方:
那么咱们这个时候会如何设计这个组件呢?
版本一
function Layout({header: Header, children}) {
return (
<div className='container'>
<div className='header'>
<Header />
</div>
<div classNmae='content'>{children}</div>
</div>
)
}
这应该是咱们比拟常见的形式,咱们通过把具体组件作为 Layout
的props
传入进来,而后依照组件的写法把它写入到组件渲染内容之中。
咱们想要应用这个组件,个别会像上面这样:
function Header() {return <h1>Title Here</h1>}
;<Layout header={Header}>
<div>content here</div>
</Layout>
那么这样做有什么问题呢?显然是有的,最显著的就是无奈在应用 Header
的时候指定props
如果 Header
有props
,那么就咱们只能硬编码在 Layout
外面,不能在应用 Header
组件的中央进行申明,所以如果咱们想要复用一个 Header
组件,咱们可能须要再申明一个组件,比方咱们给 Header
组件一个叫做 message
的prop
用来指定显示的文字内容
function Header({message = 'Title Here'}) {return <h1>{message}</h1>
}
那么如果咱们想要在不同页面复用这个组件并且显示不同的题目,咱们须要这么做:
function BigHeader() {return <Header message='The Other Title' />}
这么做显然在组件较为简单而且 props
较多的状况下,也能够达到肯定的复用成果,然而谋求极致的咱们必定不心愿仅仅局限于此。参考 React 实战视频解说:进入学习
第二版
那么有没有方法让咱们能够在应用时能指定 props
呢?答案必定是有的,咱们能够将 Layout
的header
这个 prop
接管的不是组件本体,而是具体的ReactElement
。
function Layout({header, children}) {
return (
<div className='container'>
<div className='header'>{header}</div>
<div classNmae='content'>{children}</div>
</div>
)
}
那么咱们在应用的时候就能够十分不便得指定props
<Layout header={<Header message='The Other Title' />}>
<div>Content Here</div>
</Layout>
要了解咱们能够这么做,首先咱们须要弄清楚什么是 ReactElement
。因为咱们大部分时候写React
组件的时候用的都是 JSX
,所以很多同学可能并不知道ReactElement
的存在。
其实 JSX
通过 babel
翻译之后失去的是如下代码:
// jsx
;<div id='id'>content</div>
// js
React.createElement('div', { id: 'id'}, 'content')
这个函数接管三个参数
component
具体渲染的组件,包含原生 dom 节点 (string
) 和自定义组件(object
)config
,包含所有props
再加上key
和ref
造成的字典对象children
,子节点内容,能够是ReactElement
、Array
、string
等内容
最初他返回的是一个叫做 ReactElement
类型的对象,他会蕴含后续 React 渲染过程中须要用到的一个节点蕴含的所有信息,咱们的 props.children
其实就是最典型的ReactElement
。
所以在上诉例子中,咱们传入的 header
就是一个 ReactElement
,所以能够间接作为其余节点的children
而应用。
同时应用这种形式咱们还取得来一个十分大的劣势,那就是咱们甚至能够从新定义一个组件,就能够间接应用Layout
。
<Layout header={<h1>The Other Title</h1>}>
<div>Content Here</div>
</Layout>
这样同样也是能够行得通的。
那么是否到这里咱们就功败垂成来呢?NO,NO,NO,咱们还是有值得优化的中央。
第三版
试想一下,如果咱们的 Layout
中接管来 header
是一个节点,然而呢他心愿对传入的组件的一些 props
有强制的要求呢?比方咱们的 Header
组件如果还有另外一个 prop
叫color
,用来指定文字内容的显示色彩:
function Header({message = 'Title Here', color = 'red'}) {return <h1 style={{ color}}>{message}</h1>
}
而 Layout
要求所有传入的 Header
必须色彩是 green
,显示咱们也能够在应用Header
组件的时候本人指定这个 prop
,然而如果咱们须要强制指定的prop
很多,而且应用 Layout
的中央也很多,那么显著咱们会写很多反复代码,而且如果前面咱们须要批改这个要求的时候也会导致屡次批改,甚至有些中央忘了批改而导致 bug。那么这时候咱们该怎么做呢?
咱们能够应用一个 API,这个 API 并不罕用,然而在这种场景下,他却十分有用,这就是React.cloneElement
,咱们来批改一下Layout
function Layout({header, children}) {
return (
<div className='container'>
<div className='header'>
{React.cloneElement(header, { color: 'green'})}
</div>
<div classNmae='content'>{children}</div>
</div>
)
}
通过这样,咱们真正渲染进去的 Header
他的 props.color
就永远都是green
。那么这个 API 是啥意思呢?
顾名思义,他是用来 克隆 一个 ReactElement
, 他接管三个参数,第一个是指标element
,第二个是props
,第三个是children
。可见他跟createElement
十分像,惟一的区别是第一个参数从组件变成来节点。
他做的事件其实就是拷贝指标 element
,并把前面两个参数笼罩原element
的props
,以此创立一个新的ReactElement
。
那么到此,咱们的优化过程也差不多来,当然 demo 显然是非常简单的代码,事实中的问题往往要简单很多,比方接管的如果不是一个 ReactElement
而是数组,字符串该如何解决。那么这些问题在这里就不再持续深刻来,留给各位小伙伴本人去思考吧,毕竟万变不离其宗,晓得了外围思路之后,其余问题也就能够迎刃而解来。