React Component Patterns
有狀態組件 x 無狀態組件、容器組件 x 展現組件、高階組件 x Render Callbacks(Function as Child Component)react
使用React已經有一段時間了, React——Facebook庫,使用JS構建用戶界面。本文試圖總結迄今爲止實踐中所學到的一些模式,同時但願可以幫助到即將邁入奇妙地組件世界的開發者。git
正如web服務有靜態和動態之分,React組件也有有狀態和無狀態的區分。有狀態組件--在應用中組件能夠擁有自身狀態並操縱它;無狀態組件--只接收屬性進行效果呈現。web
一個簡單的無狀態組件,只受屬性控制:編程
const Button = props => ( <button onClick={props.onClick}> {props.text} </button> );
一個具備計數功能的按鈕組件(複用上面Button組件)小程序
class ButtonCounter extends React.Component { constructor() { super() this.state = { clicks: 0 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ clicks: this.state.clicks + 1 }) } render() { return ( <Button onClick={this.handleClick} text={`You've clicked me ${this.state.clicks} times!`} /> ) } }
正如上面兩個 Demo 所示,第二個組件的 constructor 具備狀態的定義,第一個組件只是單純地渲染屬性文字。有狀態組件和無狀態組件的劃分看起來很是簡單,可是它對於組件複用具備重大意義。瀏覽器
當組件須要獲取外部數據時,咱們又能夠將組件劃分爲兩種新的類型。容器組件負責獲取數據,它經常是超出了React範疇的,如使用 Redux 或 Relay 進行了綁定。對比而言,展現型組件不依賴於程序其餘部分,它只和自身狀態或屬性有關。下面咱們實現一個用戶列表的展現組件:編程語言
const UserList = props => <ul> {props.users.map(u => ( <li>{u.name} — {u.age} years old</li> ))} </ul>
容器組件能夠用來更新用戶列表的展現:函數
class UserListContainer extends React.Component { constructor() { super() this.state = { users: [] } } componentDidMount() { fetchUsers(users => this.setState({ users })) } render() { return <UserList users={this.state.users} /> } }
這種分類將數據獲取和渲染的邏輯分開,進而使用戶列表組件能夠複用。post
若是你想了解更多該模式的信息,awesome article from Dan Abramov 對它進行了精確的解釋。學習
當你想複用組件邏輯時,高階組件很是有用。高階組件--是將組件做爲參數並返回新組件的 JS 函數
。
假設你須要構建一個可擴展菜單組件,當用戶點擊時,它會顯示隱藏子組件內容。所以,你可使用高階組件來實現:
function makeToggleable(Clickable) { return class extends React.Component { constructor() { super() this.toggle = this.toggle.bind(this) this.state = { show: false } } toggle() { this.setState(prevState => ({ show: !prevState.show })) } render() { return ( <div> <Clickable {...this.props} onClick={this.toggle} /> {this.state.show && this.props.children} </div> ) } } }
這種方法容許咱們使用 ES7裝飾器語法將邏輯應用於 ToggleableMenu 組件:
@makeToggleable class ToggleableMenu extends React.Component { render() { return ( <div onClick={this.props.onClick}> <h1>{this.props.title}</h1> </div> ) } }
如今,咱們能夠將任何子組件傳遞給ToggleableMenu組件:
class Menu extends React.Component { render() { return ( <div> <ToggleableMenu title="First Menu"> <p>Some content</p> </ToggleableMenu> <ToggleableMenu title="Second Menu"> <p>Another content</p> </ToggleableMenu> <ToggleableMenu title="Third Menu"> <p>More content</p> </ToggleableMenu> </div> ) } }
若是你熟悉 Redux 的 connect函數或者 React Router 的withRouter函數,那麼你已經使用太高階組件了。
另外一個比較高端的複用組件邏輯的方法是將函數做爲組件的 props.children,該方法也稱爲Function as Child Components
。咱們將使用渲染回調
來從新實現上面的可擴展Menu:
class Toggleable extends React.Component { constructor() { super() this.toggle = this.toggle.bind(this) this.state = { show: false } } toggle() { this.setState(prevState => ({ show: !prevState.show })) } render() { return this.props.children(this.state.show, this.toggle) } }
如今,咱們能夠將函數做爲組件的子級進行傳遞:
<Toggleable> {(show, onClick) => ( <div> <div onClick={onClick}> <h1>{props.title}</h1> </div> {show && props.children} </div> )} </Toggleable>
上面的代碼已經將一個函數做爲子組件
,可是,若咱們想複用上述邏輯,咱們須要建立一個轉換邏輯的新組件:
const ToggleableMenu = props => <Toggleable> {(show, onClick) => ( <div> <div onClick={onClick}> <h1>{props.title}</h1> </div> {show && props.children} </div> )} </Toggleable>
咱們使用Render Callbacks實現的可擴展的Menu組件以下:
class Menu extends React.Component { render() { return ( <div> <ToggleableMenu title="First Menu"> <p>Some content</p> </ToggleableMenu> <ToggleableMenu title="Second Menu"> <p>Another content</p> </ToggleableMenu> <ToggleableMenu title="Third Menu"> <p>More content</p> </ToggleableMenu> </div> ) } }
Render Callbacks和高階函數使咱們的組件更加靈活,掌握和適應起來具備必定的難度,須要反覆學習和消化。
本人能力有限,若有紕漏,請指正。
【開發環境推薦】 Cloud Studio 是基於瀏覽器的集成式開發環境,支持絕大部分編程語言,包括 HTML五、PHP、Python、Java、Ruby、C/C++、.NET 小程序等等,無需下載安裝程序,一鍵切換開發環境。 Cloud Studio提供了完整的 Linux 環境,而且支持自定義域名指向,動態計算資源調整,能夠完成各類應用的開發編譯與部署。