内容简介:复用是组件化开发体系的立命之本,可以说组件化的初衷就是为了复用性。但是组件化的复用方式也存在一定的问题,其中拆分粒度就是其中一个绕不开的话题,今天咱们就来讲一讲 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
而是数组,字符串该如何处理。那么这些问题在这里就不再继续深入来,留给各位小伙伴自己去思考吧,毕竟万变不离其宗,知道了核心思路之后,其他问题也就可以迎刃而解来。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learn Python the Hard Way
Zed A. Shaw / Addison-Wesley Professional / 2013-10-11 / USD 39.99
Master Python and become a programmer-even if you never thought you could! This breakthrough book and CD can help practically anyone get started in programming. It's called "The Hard Way," but it's re......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!