原文連接:React Component Patternsjavascript
做者:Gustavo Matheus前端
隨着 React 在前端開發中愈來愈流行,各類各樣的設計模式及新概念亦層出不窮。本文旨在總結 React 開發中一些常見的設計模式。java
React 組件能夠是有狀態的,在其生命週期內能夠操縱並改變其內部狀態;React 組件也能夠是無狀態的,它僅接受來自父組件傳入的 props,並進行展現。react
下面是一個無狀態的 Button
組件,它的行爲徹底由傳入的 props 決定:git
const Button = props =>
<button onClick={props.onClick}>
{props.text}
</button>
複製代碼
下面是一個有狀態組件(使用了上述的無狀態組件):github
class ButtonCounter extends React.Component {
constructor() {
super();
this.state = { clicks: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ clicks: ++this.state.clicks });
}
render() {
return (
<Button onClick={this.handleClick} text={`You've clicked me ${this.state.clicks} times !`} /> ) } } 複製代碼
正如你所看到的,上述 ButtonCounter
組件在 state
中維護了本身的狀態,而以前的 Button
組件僅根據 props 來進行渲染展現。這個區別看似很小,可是無狀態的 Button
組件卻高度可複用。設計模式
當與外部數據進行交互時,咱們能夠把組件分爲兩類:微信
咱們來看一個展現組件:less
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} /> } } 複製代碼
經過將組件區分爲容器組件與展現組件,將數據獲取與渲染進行分離。這也使 UserList
可複用。若是你想了解更多,這裏有一些很是好的文章,解釋地很是清楚。
當你想複用一個組件的邏輯時,高階組件(HOC)就派上用場了。高階組件就是 JavaScript 函數,接收 React 組件做爲參數,並返回一個新組件。
舉個例子:編寫一個菜單組件,當點擊一個菜單項時,展開當前菜單項,顯示子菜單。固然咱們能夠在父組件裏來控制此菜單組件的狀態,可是更優雅的方式,是使用高階組件:
function makeToggleable(Clickable) {
return class extends React.Component {
constructor() {
super();
this.toggle = this.toggle.bind(this);
this.state = { show: false };
}
toggle() {
this.setState({ show: !this.state.show });
}
render() {
return (
<div> <Clickable {...this.props} onClick={this.toggle} /> {this.state.show && this.props.children} </div> ); } } } 複製代碼
經過這種方式,咱們可使用 JavaScript 的裝飾器語法,將咱們的邏輯應用於 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
),不以 React Component
提供,而是以回調函數的形式提供。以上述 HOC 組件爲例,咱們經過渲染回調的方式重寫以下:
class Toggleable extends React.Component {
constructor() {
super();
this.toggle = this.toggle.bind(this);
this.state = { show: false }
}
toggle() {
this.setState({ show: !this.state.show });
}
render() {
return this.props.children(this.state.show, this.toggle)
}
}
複製代碼
如今,咱們能夠傳入回調函數給 Toggleable
組件做爲子結點。 咱們用新方式實現以前的 HOC 組件 ToggleableMenu
:
const ToggleableMenu = props => (
<Toggleable> {(show, onClick) => ( <div> <div onClick={onClick}> <h1>{props.title}</h1> </div> { show && props.children } </div> )} </Toggleable>
)
複製代碼
而咱們全新的 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>
);
}
}
複製代碼
是的,你沒有看錯,新的 Menu
組件同以前以HOC模式實現出來的如出一轍!
在這種實現方式下,咱們將組件內部的狀態(state
)與組件的渲染邏輯進行剝離。在上面的例子中,咱們將渲染邏輯放在了 ToggleableMenu
的渲染回調中,而展現組件的狀態(state
)依然在 Toggleable
組件內進行維護。
以上的一些例子僅僅是 React 設計模式的基礎知識。若是你想更加深刻地瞭解關於 React 設計模式的話題,如下是一些很是好的學習資料,值得一看:
關注微信公衆號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!