内容简介:复用是组件化开发体系的立命之本,可以说组件化的初衷就是为了复用性。但是组件化的复用方式也存在一定的问题,其中拆分粒度就是其中一个绕不开的话题,今天咱们就来讲一讲 React 当中的一个不太常用的 API:假如我们有一个比如:
复用是组件化开发体系的立命之本,可以说组件化的初衷就是为了复用性。但是组件化的复用方式也存在一定的问题,其中拆分粒度就是其中一个绕不开的话题,今天咱们就来讲一讲 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
较多的情况下,也可以达到一定的复用效果,但是追求极致的我们肯定不希望仅仅局限于此。
第二版
那么有没有办法让我们可以在使用时能指定 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
而是数组,字符串该如何处理。那么这些问题在这里就不再继续深入来,留给各位小伙伴自己去思考吧,毕竟万变不离其宗,知道了核心思路之后,其他问题也就可以迎刃而解来。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Definitive Guide to MongoDB
Peter Membrey、Wouter Thielen / Apress / 2010-08-26 / USD 44.99
MongoDB, a cross-platform NoSQL database, is the fastest-growing new database in the world. MongoDB provides a rich document orientated structure with dynamic queries that you’ll recognize from RDMBS ......一起来看看 《The Definitive Guide to MongoDB》 这本书的介绍吧!