如何寫一個最簡單的 Render Props?

Render Props 是 React 的一種開發模式,儘管我們都曾接觸過、都會用它(比如在 react-router 的 Route、Context的Consumer),可是我們可能不曾寫過,或是寫過了卻把它當做是一個新東西,這樣子的想法違背了 React 的核心思想。

realdennis
4 min readFeb 16, 2019

首先,我自己常看到的 Render Props 通常是這兩種形式。

  • 透過名為 render 的屬性傳遞,並且生出一個變數導入子組件並渲染,此變數是由<Something> 組件賦予的,這裡以 history 為例子:
<Route path="/" render={
({history})=> <Child prop={prop} history={history}>
}
/>
  • 透過直接嵌套在裡頭(本質上是透過 children 傳遞),Context API 就是以此方式做透過 Consumer 傳遞。
<Consumer>
{value=> <Child val={value}>}
</Consumer>

Render Props 在幹嘛?

原本的組件結構(上) & 透過Render Props的組件結構(下),另外我有筆誤,應該是<Child prop={val}/>而不是 <Children>

也就是說,只要知道這張圖,大致上可以知道我們其實把「渲染子組件」這件事拆分成「先有一個函數」、並且「透過它渲染不一樣的子組件」

那麼Render Props裡頭在做什麼呢?

實際上最後被 wrappe r起來的組件對於父組件而言只是一個新的 Child 組件,問題在於我們怎麼透過 function 把新的 Child 變出來呢?

我們從這圖知道幾件事

  1. 這個 newChild,接受一個 Function 作為參數,而且這個Function可以生成(渲染)出 Child 組件。
  2. new Child 必須提供傳入 Function(val=><Child prop={val}/>)的參數 val
  3. 這個函數會從兩個地方進來, props.render 或是 props.children

知道這些後,我們就能自己來寫一個簡單的 Render Props 了!

你組件.jsx

import Something from './Something';
<div>
<Something> { val=><Test text={val} />} </Something>
<div>

Test.jsx

export default ({text})=>(
<div>
<h1>Here is Test Component </h1>
<p> Text from Props {text} </p>
</div>)

Something.jsx

export default ({children})=>{
// children 會是一個function
// 就是 val => <Test text={val}/>
// 也就是我們怎麼“呼叫”這個函數,間接的決定Test的text props
return children('From something');
// 會把 children 渲染出來回傳
}

如果覺得看灰底黑字看到快眼脫的,就看看 codesandbox 吧!

所以 Render Props 不是新東西嗎?

不是,絕對不是,如果你沒有跳脫組件的思維,那麼你沒有辦法很徹底的了解 React,以及使用 JSX 的意義。

每一個組件可以接受 Props,且可以 Render 出其他組件,這句話聽起來不是挺耳熟的嗎?

JavaScript 中的函數可以接受參數,參數可以是函數(高階函數的用法),也可以 Return 函數出來(科里化的用法)。

components.jsx

({children})=> children(123) 
// return <Child test={123}>

甚至是用 stateful 的類組件把原始 Child 組件給 wrapped 起來,也是如此。

跟在撰寫原生 JavaScript 時,透過 Proxy ,或類似技術(比如Override),把原本要執行的函數給「加料」罷了。

--

--