理解 React 的 render props 技巧
概述
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
來取得回傳下層元件的函式,還能將共享的資料傳給下層元件,更能夠控制下層元件的渲染結果。