複用是組件化開發體系的立命之本,能夠說組件化的初衷就是爲了複用性。可是組件化的複用方式也存在必定的問題,其中拆分粒度就是其中一個繞不開的話題,今天我們就來說一講 React 當中的一個不太經常使用的 API:
cloneElement
,他如何幫組咱們更好得進行組件拆分。數組
假如咱們有一個Layout
組件,那麼通常來講這個組件主要接收的就是children
,把它放在主要內容的部分,而後組件自己的節點來控制佈局,那麼這個時候若是咱們這個佈局包含兩個部分呢,好比還有一個header
部分,是跟主要內容有明顯區分的。babel
好比:dom
那麼咱們這個時候會如何設計這個組件呢?函數
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>
}
複製代碼
那麼若是咱們想要在不一樣頁面複用這個組件而且顯示不一樣的標題,咱們須要這麼作:spa
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
而是數組,字符串該如何處理。那麼這些問題在這裏就再也不繼續深刻來,留給各位小夥伴本身去思考吧,畢竟萬變不離其宗,知道了核心思路以後,其餘問題也就能夠迎刃而解來。