React Component Design - Render Props Component
- 發佈時間
讓我們一樣以Toggle
作為例子,但這次使用Toggle
的方式要類似於下面
已複製!function Usage({ onToggle = (...args) => console.log('onToggle', ...args) }) { return ( <Toggle onToggle={onToggle}> {({ on, toggle }) => ( <div> {on ? 'The button is on' : 'The button is off'} <Switch on={on} onClick={toggle} /> <hr /> <button aria-label="custom-button" onClick={toggle}> {on ? 'on' : 'off'} </button> </div> )} </Toggle> ); }
要符合這樣的使用方式其實不難,程式碼如下
已複製!import React from 'react'; import { Switch } from '../switch'; class Toggle extends React.Component { state = { on: false }; toggle = () => this.setState( ({ on }) => ({ on: !on }), () => this.props.onToggle(this.state.on) ); getStateAndHelpers() { return { on: this.state.on, toggle: this.toggle, }; } render() { return this.props.children(this.getStateAndHelpers()); } }
可以看到render function
把Toggle
的on
state 跟toggle
傳給children
,由children
自行決定要render
什麼東西,這樣的方式更有彈性可以決定要顯示什麼樣子的 component,也可以明確知道on
跟toggle
是來自於Toggle
。跟Compound component
的設計方式比起來,個人覺得這樣的方式有幾個優點:
- 少了複雜的 Provider/Context state 傳遞關係
- 不用 validate children 是不是有在
Toggle
的定義介面裡
但缺點也很明顯,雖然這樣的設計方式保持最大的彈性讓 children 自行決定要 render 什麼樣的東西,但這既是優點也是缺點,因為這樣的 component 所持有的邏輯過少,children 還是需要自行決定顯示的內容。另外一個缺點則是如果 component 都採用這樣的方式,則會讓 component 的層級過於多層,底層的 children component 最終會很難排查 props 是從何而來。
雖然還沒介紹到HOC component
的設計方式,但這邊引述一下react-router
的作者 Michael Jackson 提到這樣做比HOC component
好的原因還有:
- 解決了 naming collision 的問題
- 跟
HOC component
比起來提供更多的彈性可以決定render
什麼樣的東西,換句話說,這樣的方式直接可以取代HOC component
,但是,HOC component
卻沒辦法取代Render props
之後等介紹到HOC component
的時候會再更詳細說明優缺點。