- 原文地址:Pass Multiple Children to a React Component with Slots
- 原文作者:Dave Ceddia
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:Zheng7426
- 校對者:noahziheng, BillShiyaoZhang
假如你需要寫一個可以重複使用的元件。可是呢,名為 children
的 prop 不能解決這個需求。這個元件得有能力接收不止一個 children,而且這些 children 的放置還不是相鄰的,而是按照需求而定。
可能你在寫的是帶有一個標題、一個邊欄和一個內容區塊的名為 Layout
的元件。又或者你正巧在寫一個帶有左右兩側動態邊欄的 NavBar
元件。
以上這些任務都可以輕鬆地藉助 “插槽” 模式完成,換言之就是傳遞 JSX 到一個 prop 中去。
小結:你可以把 JSX 傳向任何而不只是叫 children
的prop,也並不侷限於通過在一個元件的標籤裡嵌入 JSX —— 從而在簡化資料傳遞的同時呢,也讓元件有更多被重複使用的價值。
快速回顧 React 裡的 Children
我們們得先共同瞭解這樣一個事實:React 能夠讓你通過在 JSX 標籤之間巢狀 children 的方式來向元件傳遞 children。這些元素 (零個、一個或多個)在那個元件裡將以名為 children
的 prop 的形式存在。 React 的名為 children
的 prop 其實相當於 Angular 的 transclusion 還有 Vue 的 <slot>
。
這裡有一個向 Button
元件傳遞 children 的例子:
<Button>
<Icon name="dollars"/>
<span>BUY NOW</span>
</Button>
複製程式碼
我們們詳細地探究一下 Button
這個元件的實現,並看看它對 children 都做了什麼:
function Button(props) {
return (
<button>
{props.children}
</button>
);
}
複製程式碼
Button
有效地將你之前傳的東西用一個 button
元素封裝了起來。到這一步雖然沒有什麼石破天驚的,但這卻是一個實用的能力。它讓接收資訊的元件得以把 children 放置在佈局的任何位置,或是為了變換風格樣式而將其封裝在一個The className
裡。被渲染之後的 HTML 程式碼看起來會是這樣子:
<button>
<i class="fa fa-dollars"></i>
<span>BUY NOW</span>
</button>
複製程式碼
(順便一提,這裡我們們假設 Icon
元件渲染出了 <i>
標籤。)
Children 也是一個普通的 Prop
React 針對 children 的這個用法挺炫的:被巢狀的元素可以指定成名為 children
的 prop,然而這並不是一個神奇而特殊的 prop 。你可以給一個 prop 指定任何其他的元素。且看:
// 這行程式碼其實 ——
<Button children={<span>Click Me</span>} />
// 相當於以下的程式碼。
<Button>
<span>Click Me</span>
</Button>
複製程式碼
所以你不僅能夠像傳遞一個普通的 prop 那樣去傳遞 children
,還可以往 prop 裡傳遞 JSX 程式碼。意不意外?沒錯,這個功能並不專屬於名為“children”的 prop!
以命名的卡槽來使用 Props
如果我告訴你,你可以向任何 prop 傳遞 JSX 程式碼,你會咋想?
(你已經知道這個祕密了,不是嗎?)
這裡有一個使用這些 “卡槽” 的 props 的例子 —— 以 3 個 props 呼叫一個名為 Layout
的元件:
<Layout
left={<Sidebar/>}
top={<NavBar/>}
center={<Content/>}
/>
複製程式碼
在這個名為 Layout
的元件之中,它可以隨心所欲地使用 left
,top
以及 center
這三個 prop。以下是一個簡單的例子:
function Layout(props) {
return (
<div className="layout">
<div className="top">{props.top}</div>
<div className="left">{props.left}</div>
<div className="center">{props.center}</div>
</div>
);
}
複製程式碼
試想一下, Layout
元件內的程式碼可以變得十分複雜,它可以擁有很多巢狀的 div
或 Bootstrap 類名等等。這個元件亦可以給更細緻的元件傳遞資訊。 無論 Layout
需要做什麼,它的使用者只需要知道如何傳遞 left
,top
以及 center
這三個 prop 就夠了。
使用 Children 來直接傳遞 Props
關於傳遞 children還有一個很不錯的特性(無論使用的 prop 是否為 children
):當你打算傳遞 child prop 的時候,正好在 parent 的作用域內,這時你可以向下傳遞任何你所需要的資訊。
這就好比 躍過了一個層級。舉個例子:與其去傳遞一個 "user" 給 Layout 元件,然後讓 Layout 再將 "user" 傳向 NavBar 元件,還不如直接建立一個 NavBar(已經設好 user)然後把整個 NavBar 傳向 Layout。 這可以幫助避免 “prop 鑽井” 的問題,你不必再費心地將一個 prop 放進好多個元件的層級。
function App({ user }) {
return (
<div className="app">
<Nav>
<UserAvatar user={user} size="small" />
</Nav>
<Body
sidebar={<UserStats user={user} />}
content={<Content />}
/>
</div>
);
}
// 接收 children 並渲染它(們)
const Nav = ({ children }) => (
<div className="nav">
{children}
</div>
);
// Body 需要一個側邊欄和內容,如果像下面這樣寫的話,
// 他們可以做任何事
const Body = ({ sidebar, content }) => (
<div className="body">
<Sidebar>{sidebar}</Sidebar>
{content}
</div>
);
const Sidebar = ({ children }) => (
<div className="sidebar">
{children}
</div>
);
const Content = () => (
<div className="content">main content here</div>
);
複製程式碼
現在和下面這個寫法比較一下,Nav 和 Body 都接受名為 user
的 prop,然後它們負責手動將 prop 傳遞給 children。在那之後,它們的 children 必須給更細一層的 children 傳遞下去……
function App({ user }) {
return (
<div className="app">
<Nav user={user} />
<Body user={user} />
</div>
);
}
const Content = () => <div className="content">main content here</div>;
const Sidebar = ({ user }) => (
<div className="sidebar">
<UserStats user={user} />
</div>
);
const Body = ({ user }) => (
<div className="body">
<Sidebar user={user} />
<Content user={user} />
</div>
);
const Nav = ({ user }) => (
<div className="nav">
<UserAvatar user={user} size="small" />
</div>
);
複製程式碼
沒有之前那個寫法方便,不是嗎?用這種方法向下傳遞 prop (又稱 “prop 鑽井”)會讓元件之間被太多你可能不想要的麻煩所羈絆 —— 並不一定總是壞事,但你最好搞清楚它們之間都是怎麼連線的。上面第一種使用 children 的寫法可以避免對更復雜解決辦法的需求,比如說可能要用到 context, Redux 或 MobX (還有好多別的)。
要當心 PureComponent 或 shouldComponentUpdate
如果你需要在一個帶有 children 的元件上實現 shouldComponentUpdate
(抑或 PureComponent
),用來防止二次渲染,同時 children 也無法被渲染出來。所以要注意這一點。在實際操作中,帶有 “插槽” 的元件很有可能體積小而且渲染速度快,所以不太會需要效能方面的調整。
不過如果你遇到了確實需要調整 “插槽” 元件的效能的情況,那麼可以考慮把表現效能過慢的部分程式碼提取出來,單獨放進一個元件,然後進行調整。
學習 React 有時會很痛苦 —— 程式碼庫和工具實在太多啦! 想聽我的意見嗎?那就是將那些程式碼庫和工具通通忽略掉 :) 若閣下想要步步為營的引導,就請閱讀我寫的書吧 —— Pure React。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。