理解 React 的 render props 技巧

Zong-Rong Huang
9 min readJul 5, 2021

--

Photo by Julia Kadel on Unsplash

概述

render props 是 React 中較高階的 coding 手法。初次接觸時,可能會覺得這樣的寫法不易理解。

本文會先說明 React 中 prop 如何傳遞資料,再來說明 render prop 模式,最後再說明另一種類似的手法 children prop。

主要內容:

React 元件用 props 傳遞資料

React 元件經常透過各種 prop 傳送資料。傳送方向通常是由上層元件傳給下層元件。居中扮演關鍵角色的就是 React 提供給元件的介面。

在下面例子中,可以看到 ParentComponent 元件從上層元件 (即 App) 內部取得 user 資料:

// App.js
import ParentComponent from './ParentComponent'
export default App () {
const user = {name:..., age: ...}

// React 封裝後的元件
// 作為 React 給予的特殊介面,能夠取得 user prop
<ParentComponent user={user} />
}

<ParentComponent user={user} /> 看起來是個自訂標籤,但其實可以看成是 React 提供的介面。透過這層介面,元件可以從外部拿到名為 user 的資料。從介面拿到資料後,就可以拉進元件的內部使用:

// ParentComponent.js// 從介面取得 user prop 後,將資料拉到元件內部使用
export default ParentComponent = ({user}) => (
<h1>Welcome back, {user.name}!</h1>
)

還可以進一步將資料往下層元件傳送,讓上下層元件共享資料:

// ParentComponent.js
import ChildComponent from './ChildComponent'
// user 資料從 ParentComponent 內部傳給 ChildComponent 的介面
export default ParentComponent = ({user}) => (
<>
<h1>Welcome back, {user.name}!</h1>
<ChildComponent user={user}/>
</>
)

簡而言之,可以將 React 元件想像成生物細胞,就更能掌握資料的流向:

細胞藉由細胞膜從外部取得營養、氧氣和化學訊號,再將這些東西拉進細胞內部利用。React 元件則是從介面上拿到資料,再將資料傳進內部運用。

props 可傳遞資料和函式

React 的 props 不僅能傳遞資料,還能傳遞函式。這是因為 JavaScript 函式具備一級函式 (first-class function) 特性,因此可以和字串、物件一樣透過 prop 傳遞。

render props 就是利用 props 能傳遞函式的特性,發展出來的高階 coding 技巧:

A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.

簡短地說,一個 render prop 有以下兩個特性:

  • 這個 prop 負責傳遞函式
  • 傳遞的函式負責回傳另一個 React 元件
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>

在上面的例子中,叫作 render 的 prop 傳送的是函式 data => (<h1>Hello {data.target}</h1>),負責回傳一個 <h1></h1> 元件。

此類 prop 目的在於渲染出另一個元件,因此名稱通常取作 render,連帶讓這個 coding 技巧被稱為 render props。

要注意的是,這個 prop 的名稱可以自訂,不一定要叫 render,取名叫 renderChild 之類任何名稱都行!(個人覺得 render 很容易和 React 的 render() 方法搞混,或是讓人誤會成是 React 內建的特殊 prop。)

用 render props 模式傳遞函式

React 的資料流向主要是由上層元件往下層元件傳遞。因此,採用從上而下的角度會更好理解 render props 模式。

先來觀察一下程式的上下文吧:

// App.js
const Parent = (...) => {...}
const Child = (...) => {...}
export default function App() {
const familyName = 'Jackson'

return (
<div className="App">
<Parent
familyName={familyName}
renderChild={data => <Child familyName={data}/>}
/ >
</div>
);
}

renderChild prop 是本文所要探討的重點:renderChild 攜帶一個函式,而這個函式負責回傳 Child 元件。此外,這個函式而且會將 data 參數接收到的值傳進 <Child/> 中的 familyName prop,讓兩個元件顯示相同的姓氏。

接著到最重要的 Parent 元件:

// Parent 元件
const Parent = ({familyName, renderChild}) => {
return (
<div>
Parent (family name: {familyName})

// 傳入 familyName 並執行 renderChild 函式
{renderChild(familyName)}
</div>
)
}

Parent 從自己的 props 物件中取出 familyName 字串和 renderChild 函式。從 props 的內容可以看出,renderChild 對應到剛才的函式。

// Parent 的 props 物件
props: {
renderChild: data => <Child familyName={data}/>,
...
}

renderChild 函式在 Parent 元件內執行,並以 familyName 作為函式的引數 (argument),然後回傳/渲染出 Child 元件。

接下來,把焦點轉向最底層的 Child 元件看看它怎麼使用剛才傳入的 familyName

// Child 元件
const Child = ({familyName}) => {
return (
<div>
Child (family name: {familyName})
</div>
)
}

結果如下:

總的來說,上層元件取得 render prop 攜帶的函式後,就可以在自己內部執行這個函式,進而回傳出下層元件。同時,還可以把共享的資料放到函式裡,讓下層元件也能使用。

用 children prop 傳送函式

除了透過 prop 來傳遞能夠回傳元件的函式外,另一個極相似的方法是透過 props.children 來傳遞函式。

認識 props.children

props.children 是 React 元件內建的屬性,用來紀錄元件起始標籤和結尾標籤之間所有的內容,從字串到函式都可以紀錄:

<Component>
// 所有出現在元件中的內容都會存到 props.children 屬性
abcdefg
{() => {console.log('example')}}
...
</Component>

props.children 內含有多項內容時,會以陣列形式把內容儲存起來。要取得特定內容使用時,記得使用陣列方法:

const Component = ({children}) => {
const str = children[0] // "abcdefg"
const func = children[1] // () => {console.log('example')}
return (...)
}

使用 props.children 來傳送函式

現在就來改寫剛才的範例,改用 props.children來傳送能夠回傳元件的函式:

// App.js
const Parent = ({familyName, children}) => (
<div>
Parent (family name: {familyName})

// 傳入 familyName 並執行 children 指涉的函式
{children(familyName)}
</div>
)
const Child = (...) => {...}export default function App() {
const familyName = 'Jackson'

return (
<div className="App">
<Parent familyName={familyName} />
// 要傳送的函式放到標籤之間
{data => <Child familyName={data}/>}
</Parent>
</div>
);
}

主要的改變是將要回傳元件的函式放到 <Parent> 元件標籤之間。接著在 Parent 元件中取出 children 紀錄的函式,將 familyName 資料傳進函式內,就能回傳出所需的 Child 元件。

結果如下:

結論

render props 是 React 比較高階的 coding 手法,主要精神是讓元件透過一般的 prop 或是 props.children 來取得回傳下層元件的函式,還能將共享的資料傳給下層元件,更能夠控制下層元件的渲染結果。

--

--

Zong-Rong Huang

Frontend web developer/technical writer that writes to learn and self-entertain. I’m based in Taiwan.