寫過react項目的應該都碰到過,不一樣組件複用相同代碼的問題,在react早期使用React.createClass建立組件的時代,咱們常用的是mixins來實現代碼複用。好比有個組件A,它用來實時的獲取鼠標的位置。react
//A組件 import React from 'react' import ReactDOM from 'react-dom' const App = React.createClass({ getInitialState() { return { x: 0, y: 0 } }, handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }) }, render() { const { x, y } = this.state return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <h1>The mouse position is ({x}, {y})</h1> </div> ) } }) ReactDOM.render(<App/>, document.getElementById('app'))
若是此時有個組件B也想集成這個功能,咱們能夠經過mixins,代碼是這樣的git
//B組件 import React from 'react' import ReactDOM from 'react-dom' const MouseMixin = { getInitialState() { return { x: 0, y: 0 } }, handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }) } } const App = React.createClass({ // Use the mixin! mixins: [ MouseMixin ], render() { const { x, y } = this.state return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <h1>The mouse position is ({x}, {y})</h1> </div> ) } }) ReactDOM.render(<App/>, document.getElementById('app'))
很容易是吧~但委屈的是react16以後就再也不支持mixins了,由於es6普及了呀!es6
那怎麼辦呢?辦法老是有的,HOC(高階組件)的概念被提出來,什麼是高階組件?說白了其實就是把函數當作參數傳入到另外一個函數中,在咱們展開高階組件前,咱們先來想一想除了由於es6的普及react再也不支持mixins以外,mixins還有啥其它缺點不。固然是有的,有如下幾點:github
因此爲了代替mixins,不少人就提出了HOC(高階組件),代碼是下面這樣的。react-router
import React from 'react' import ReactDOM from 'react-dom' const withMouse = (Component) => { return class extends React.Component { state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <Component {...this.props} mouse={this.state}/> </div> ) } } } class App extends React.Component{ render() { // 代替直接處理state,咱們從props裏得到x,y座標 const { x, y } = this.props.mouse return ( <div style={{ height: '100%' }}> <h1>The mouse position is ({x}, {y})</h1> </div> ) } } //把App組件當作參數傳到withMouse方法裏面,在withMouse內部經過props得到x、y座標值 const AppWithMouse = withMouse(App) ReactDOM.render(<AppWithMouse/>, document.getElementById('app'))
看起來很不錯的樣子!app
可是,回到以前mixins存在的問題,咱們想想,HOC有上述的問題麼?咱們來看下:<br/>dom
個人天.....函數
幸運女神降臨!
mmp,前面都是炮灰,到了劃重點的時候啦!<br/>學習
Render Prop是個值爲函數的屬性,經過Render Prop,組件知道什麼應該被渲染
很糊塗是否是,看代碼ui
import React from 'react' import ReactDOM from 'react-dom' import PropTypes from 'prop-types' class Mouse extends React.Component { static propTypes = { render: PropTypes.func.isRequired } state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ) } } const App = React.createClass({ render() { return ( <div style={{ height: '100%' }}> <Mouse render={({ x, y }) => ( <h1>The mouse position is ({x}, {y})</h1> )}/> </div> ) } }) ReactDOM.render(<App/>, document.getElementById('app'))
看明白了麼,這裏咱們經過定義一個render屬性,值是個函數,描述了咱們想要渲染的元素,而後在子組件裏面調用該render方法,再回頭看下以前的兩個問題,難以溯源,如今主動權在父組件上,我要什麼數據大家給我拿來就好了,大家子組件各自去實現,我只要結果不要過程,於是就不存在數據來源問題,命名空間的問題也沒了。好厲害~~~。
最後偷偷的告訴大家一個更厲害的,上面的render方法裏面咱們是直接寫出了渲染x,y值,只適用於當前App組件,咱們能夠經過高階組件來達到爲任何組件添加該功能,代碼是這樣的。
const withMouse = (Component) => { return class extends React.Component{ render() { return <Mouse render={mouse=>( <Component {...this.props} mouse={mouse}/> )}/> } } }
聽說react-router源碼裏面爲每一個組件增長路由屬性就是經過該方法!
好了!大功完成了,歡迎一塊兒討論學習~
我的博客地址:意卿