在軟件開發的過程當中Code Reuse和可讀性一直是開發人員致力於解決的問題,在React社區中,以往出現了不少的Pattern來解決這個問題,例如Mixin,HOC等等。Render Props是React社區中裏提出的另外的一種Pattern。由React Router的Co-author Michael Jackson 提出,它與HOC等有什麼區別呢?又解決了什麼問題呢?在這篇文章來一探究竟。html
全部的Pattern或者solution都是爲了解決問題而提出的,那麼render props究竟是爲了解決什麼問題呢?咱們先來看一個例子。例如在咱們的App裏咱們實現了這樣一個componentreact
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 0,
y: 0
};
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> <p> The mouse is at ({this.state.x}, {this.state.y}) </p> </div>
);
}
}
複製代碼
Code Sandboxapp
熟悉React的同窗都知道這是一個很簡單的App,他能夠追蹤鼠標位置給到本身。可是問題來了,萬一哪天你的朋友看到這個component,說這個feature很Cool,他的app裏能不能用上呢?那麼該怎麼解決這個問題呢?dom
首先第一種想法很直接,能夠把這個component作爲一個父Component,另一個Component做爲一個子Component。來看代碼。函數
import React from 'react';
const Dog = ({mouse}) => (
<div>{mouse.x}, {mouse.y}</div>
)
class MouseDog extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 0,
y: 0
};
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> <p> The mouse is at ({this.state.x}, {this.state.y}) <Dog mouse={mouse} /> </p> </div> ); } } 複製代碼
如今咱們有了兩個Component, Dog 和 DogWithMouse,其實Dog做爲DogWithMouse的子Component,那麼DogWithMouse Component就能利用鼠標位置。好像這樣是把問題解決了,可是若是咱們有另一個Component也想要trace mouse這個feature怎麼辦呢?難道再從新這樣寫一個Component嗎?顯然這種方式是不可取的。this
若是熟悉react的同窗,可能會想到,這很簡單呀,咱們寫一個High order Component(HOC)就好了,的確HOC能夠解決這個問題,咱們來看看HOC的代碼是怎麼樣的。spa
import React from 'react';
const withMouse = (Component) => {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 0,
y: 0
};
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> <Component mouse={this.state} {...this.props} /> </div> ); } } } const Dog = ({ mouse }) => <div>{mouse.x}, {mouse.y}</div> const EnhancedDog = withMouse(Dog) 複製代碼
在上面的代碼裏,咱們建立了一個withMouse的HOC, 這樣就把這部分邏輯抽象了出來。其餘的Component想要使用咱們mouse tracking的feature。只要將Component傳入HOC就能獲得一個enhance過的Component。pretty cool, right?若是對於HOC不太瞭解,能夠參考react 的官方文檔進行了解。 HOCdebug
HOC彷佛已經完美的解決了問題,可是這種方式有沒有什麼缺點呢?彷佛是有下面的一些缺點。code
間接的props傳入。 讓咱們從新看一看Dog Component,他的props傳入了mouse,可是在使用這個Component的時候,並無地方傳入了mouse。這是由於mouse是在HOC裏傳入的。你大概會納悶這個Mouse是從那來的。若是維護一個大項目的時候,有時候你就會發現這種間接的props傳入,是有多麼坑了。component
命名衝突的問題。 用過react的同窗不知道有沒有經歷過大量使用HOC的場景,筆者是見過多層HOC嵌套使用的項目代碼。就像這樣: const NewComponent = withA(withB(withC(...(Component))))
若是你用過這樣的多層嵌套的HOC,那麼就可能會發生命名衝突的問題。來看代碼:
const withB = (Component) => {
return class extends React.Component {
render() {
return (
<div style={{ height: 800 }} > <Component mouse={'hehe'} {...this.props} /> </div> ); } } } const EnhancedDog = withB(withMouse(Dog)) 複製代碼
在這裏定義另外的一個HOC withB。它也給props上傳了一個一樣的mouse, 若是再用它來wrap withMouse(Dog)
時,你會發現mouse tracking的feature就不work,可是React不會有任何提示,這是命名衝突的問題。若是你有一個七八層的wrapp到一塊兒的Component,若是出現這樣的命名衝突的問題,那麼就有的debug了。
好了,既然HOC有這樣的問題,那有沒有什麼方式既能夠作到code reuse,同時也能夠避免HOC的這些問題呢?答案就是Render Props。Render Props是React Traning團隊提出的一種新的React中組織Component的方式,同時也是十分的簡單,咱們來看上邊的例子中,若是用render props如何解決code reuse的問題。
import React from 'react';
import { render } from "react-dom";
class Mouse extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 0,
y: 0
};
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div>
);
}
}
const DogMouse = () => (
<Mouse render={({x, y}) => (<div>this mouose is ({x}, {y})</div>})> ) render(<DogMouse/>, document.getElementById('app')) 複製代碼
上邊就是render props的一個簡單例子,咱們看完會發現特別的簡單,沒什麼特別的。Mouse就是一個普通的Component,惟一不一樣的是這個Component上定義了一個叫render的prop,是一個函數。在Mouse中會將state做爲參數調用這個函數。而建立的DogMouse一樣也是一個普通的Component,其中定義render的方法,用於定義要render什麼Component。咱們看看render props和HOC相比解決了什麼問題:
code reuse。和HOC同樣的,render props一樣解決了code reuse的問題。把mouse tracking的feature抽象成爲一個Mouse的Component,若是有另一個Component像複用這個feature的時候,簡單的定義另一個Component定義相應的render props就行了。
間接的props傳入,render props 沒有這個問題,能夠清楚的看到props(x, y)是從什麼地方出入的.
命名衝突。利用render props是沒有Component wrapp的,因此除非定義Component時候本身命名重複,不然不會有命名衝突的問題。
從上面的例子上看,render props能夠再大多數狀況下替代HOC。react的官方文檔上,目前也正式的介紹了render props,下次讓你想用HOC的時候,來試一試Render props把。