高階組件在React應用中,很是很是重要。空有一個想學好React的心,卻沒有一個好的教程。但願這篇文章能夠幫組到你,把React學的更好。經過這篇文章你能夠學到高階組件的定義及原理、高階組件在項目中的常見應用、通用高階組件如何封裝以及繼承方式高階組件和代理方式高階組件。react
create-react-app myapp建立一個react項目,並在src目錄下新建一個components放置組件A、B、C三個組件。git
將A組件做爲高階組件github
import React, { Component } from 'react'
function A(WrappedComponed) {
return class A extends Component {
render() {
return (
<div className="a-container">
<div className="header">
<div>提示</div>
<div>x</div>
</div>
<div>
<WrappedComponed />
</div>
</div>
)
}
}
}
export default A
複製代碼
B組件和C組件做爲參數傳遞給A數組
import React, { Component } from 'react'
import A from './A'
class B extends Component {
render() {
return (
<div>
這是組件B
<img src={require('../images/B.png')} alt=""/>
</div>
)
}
}
export default A(B)
複製代碼
A組件其實就是一個function,經過接受一個參數返回一個react組件,而接收的參數又是一個組件,這就是一個簡單的高階組件。高階組件就是接受一個組件做爲參數並返回一個新組件的函數,高階組件是一個函數,並非一個組件。高階組件帶來的好處是多個組件都須要某個相同的功能,使用高階組件減小重複的實現,好比咱們上述的B/C組件都須要A。最後效果bash
高階組件能夠看作是裝飾器模式(Decorator Pattern)在React的實現。即容許向一個現有的對象添加新的功能,同時又不改變其結構,屬於包裝模式(Wrapper Pattern)的一種 ES7中添加了一個decorator的屬性,使用@符表示,能夠更精簡的書寫。但在create-react-app中並不直接支持,你們能夠自行googleapp
建立一個D組件函數
import React, { Component } from 'react'
function d(WrappedComponent) {
return class D extends Component {
render() {
return (
<div>
這是高階組件
<WrappedComponent />
</div>
)
}
}
}
export default d
複製代碼
使用裝飾器@學習
import React, { Component } from 'react'
// import A from './A'
import d from './D'
@d
class B extends Component {
render() {
return (
<div>
這是組件B
<img src={require('../images/B.png')} alt=""/>
</div>
)
}
}
export default B
複製代碼
效果以下圖:ui
若是學到這裏你們應該都學會了如何建立和使用高階組件了,可是高階組件就是這一點點知識嗎?答案確定是NO,接下來讓咱們一塊兒看看在實戰中是如何應用高階組件的。返回的新數組類直接繼承React.Component類,新組件扮演的角色傳入參數組件的一個代理,在新組件的render函數中,將被包裹組件渲染出來,除了高階組件本身要作的工做,其他功能所有轉手給被包裹的組件。this
代理方式的高階組件主要有如下四個方面的運用: 操縱prop、訪問ref、抽取狀態、包裝組件
修改下A組件,代理方式
import React, { Component } from 'react'
export default (title) => WrappedComponent => class A extends Component {
render() {
return (
<div className="a-container">
<div className="header">
<div>{title}</div>
<div>x</div>
</div>
<div>
<WrappedComponent {...this.props}/>
</div>
</div>
)
}
}
複製代碼
在B中添加props:
import React, { Component } from 'react'
import A from './A'
class B extends Component {
render() {
return (
<div>
這是組件B
<br />
個人名字叫: {this.props.name}
個人年齡是: {this.props.age}
<img src={require('../images/B.png')} alt=""/>
</div>
)
}
}
export default A('提示')(B)
複製代碼
如今咱們要作的是經過高階組件對組件B屬性進行修改。咱們先添加一個性別組件。咱們不在APP.js中經過
<B name={'張三'} age={'18'} />
這樣的方式將性別引入,而是在咱們的高階組件A中進行操做
上面講述的是屬性的增長,那麼屬性的刪減呢<WrappedComponent sex={'男'} {...this.props} />
import React, { Component } from 'react'
export default (title) => WrappedComponent => class A extends Component {
render() {
const {age, ...otherProps} = this.props
return (
<div className="a-container">
<div className="header">
<div>{title}</div>
<div>x</div>
</div>
<div>
<WrappedComponent sex={'男'} {...otherProps} />
</div>
</div>
)
}
}
複製代碼
這樣在咱們的otherProps中是沒有age這個屬性的,所以就達到了屬性的刪減。
咱們在C組件中定義一個getName方法,
getName() {
return '我是C組件'
}
複製代碼
可是怎麼在高階組件A中調用到呢?其實i很簡單就是在高階組件中添加ref
import React, { Component } from 'react'
export default (title) => WrappedComponent => class A extends Component {
refc(instance) {
instance.getName && alert(instance.getName())
} // instanc:WrappedComponent組件的實例
render() {
const {age, ...otherProps} = this.props
return (
<div className="a-container">
<div className="header">
<div>{title}</div>
<div>x</div>
</div>
<div>
<WrappedComponent sex={'男'} {...otherProps} ref={this.refc.bind(this)} />
</div>
</div>
)
}
}
複製代碼
打印的我是C組件其實就是咱們在C組件中定義的getName方法。經過這種方法能夠操做任何被包裹組件的方法,甚至操做任何一個DOM。
在B組件中增長一個輸入框
import React, { Component } from 'react'
import A from './A'
class B extends Component {
constructor(props) {
super(props)
this.state = {
value: ''
}
}
changeInput(e) {
console.log(e)
this.setState({
value: e.target.value
})
}
render() {
return (
<div>
這是組件B
<input type='text' value={this.state.value} onInput={this.changeInput.bind(this)}/>
<br />
個人名字叫: {this.props.name}
個人年齡是: {this.props.age}
<br />
個人性別是: {this.props.sex}
<img src={require('../images/B.png')} alt=""/>
</div>
)
}
}
export default A('提示')(B)
複製代碼
單個組件的狀態書寫方式,若是不少組件都須要input,那麼就會重複代碼,所以咱們須要將狀態抽離到高階組件A中。
import React, { Component } from 'react'
export default (title) => WrappedComponent => class A extends Component {
refc(instance) {
// instance.getName && alert(instance.getName())
}
constructor(props) {
super(props)
this.state = {
value: ''
}
}
changeInput= (e) => {
this.setState({
value: e.target.value
})
}
render() {
const { age, ...otherProps } = this.props
const newProps = {
value: this.state.value,
onInput: this.changeInput
}
return (
<div className="a-container">
<div className="header">
<div>{title}</div>
<div>x</div>
</div>
<div>
<WrappedComponent {...newProps} sex={'男'} {...otherProps} ref={this.refc.bind(this)} />
</div>
</div>
)
}
}
複製代碼
在B組件咱們接受一個newProps狀態
<input type='text' {...this.props}/>
回到頁面,發現跟上面的是同樣,這樣咱們就將組件的狀態抽離出來了,若是C組件須要input,只須要將添加一個input輸入框就好了。極大的簡化了代碼。
採用繼承關聯做爲參數的組件和返回的組件,加入傳入的組件參數是WrappedComponent,那麼返回的組件就是直接繼承自WrappedComponent
經過代碼的對比,咱們不難發現代理方式的高階組件和繼承方式的高階組件的區別:新建一個E繼承高階組件
import React, { Component } from 'react';
const modifyPropsHOC = (WrappedComponent) => class NewComponent extends WrappedComponent {
render() {
const element = super.render()
const newStyle = {
color: element.type === 'div' ? 'red': 'green'
}
const newProps = { ...this.props, style: newStyle }
return React.cloneElement(element, newProps,element.props.children)
}
}
export default modifyPropsHOC
複製代碼
在F、G組件中使用繼承組件
import React, { Component } from 'react'
import E from './E'
@E
export default class G extends Component {
render() {
return (
<p>
我是p
</p>
)
}
}
複製代碼
這就是咱們經過繼承方式的高階組件來操縱props。高階組件須要根據參數來渲染組件,不建議使用。
在G組件中
import React, { Component } from 'react'
import E from './E'
@E
export default class G extends Component {
componentWillMount() {
alert('我是原始生命週期')
}
render() {
return (
<p>
我是p
</p>
)
}
}
複製代碼
在繼承高階組件E中修改G中的屬性
import React, { Component } from 'react';
const modifyPropsHOC = (WrappedComponent) => class NewComponent extends WrappedComponent {
componentWillMount() {
alert('我是更改生命週期')
}
render() {
const element = super.render()
const newStyle = {
color: element.type === 'div' ? 'red': 'green'
}
const newProps = { ...this.props, style: newStyle }
return React.cloneElement(element, newProps,element.props.children)
}
}
export default modifyPropsHOC
複製代碼
高階組件最大的好處就是解耦和靈活性,在react的開發中仍是頗有用的。 固然這不多是高階組件的所有用法。掌握了它的一些技巧,還有一些限制,你能夠結合你的應用場景,發散思惟,嘗試一些不一樣的用法。
你能夠跟着文章嘗試一遍,也能夠直接clone項目到本地跑跑。項目地址:React-hightComponet學習
固然也建議去慕課網觀看宋老師的詳細教學視頻慕課網地址