1.mixins
寫過react專案的應該都碰到過,不同元件複用相同程式碼的問題,在react早期使用React.createClass建立元件的時代,我們經常使用的是mixins來實現程式碼複用。比如有個元件A,它用來實時的獲取滑鼠的位置。
//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,程式碼是這樣的
//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普及了呀!。
那怎麼辦呢?辦法總是有的,HOC(高階元件)的概念被提出來,什麼是高階元件?說白了其實就是把函式當做引數傳入到另一個函式中,在我們展開高階元件前,我們先來想想除了因為es6的普及react不再支援mixins之外,mixins還有啥其它缺點不。當然是有的,有以下幾點:- 難以溯源,mixins修改state,在元件內部你無法知道state從哪來,尤其是有多個mixins的情況。
- 名稱空間,多個mixins修改同一個state導致的命名衝突。
2.HOC(高階元件)
所以為了代替mixins,很多人就提出了HOC(高階元件),程式碼是下面這樣的。
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'))
複製程式碼
看起來很不錯的樣子!
但是,回到之前mixins存在的問題,我們想一想,HOC有上述的問題麼?我們來看下:- 難以溯源,跟mixins不同的是,我們不再糾結state的源頭,我們現在要糾結的是HOC的props裡提供了些啥...
- 名稱空間的衝突,這個問題依然存在,props裡屬性名可能會被多個HOC重複使用。
我的天.....
3.Render Prop
幸運女神降臨!
mmp,前面都是炮灰,到了劃重點的時候啦!
Render Prop是個值為函式的屬性,通過Render Prop,元件知道什麼應該被渲染
很糊塗是不是,看程式碼
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原始碼裡面為每個元件增加路由屬性就是通過該方法!
好了!大功完成了,歡迎一起討論學習~
個人部落格地址:意卿