知識(shí)的學(xué)習(xí)一定要誠信于自己。
高階函數(shù)的基本概念
1.函數(shù)可以作為參數(shù)被傳遞,如:
setTimeout(()=>{
console.log(1)
},1000)
2.函數(shù)可以作為返回值被輸出,如:
function foo(x){
return function(){
return x;
}
}
高階組件的基本概念(High Order Component,HOC)
1.高階組件就是接受一個(gè)組件作為參數(shù)并返回一個(gè)新的組件
2.高階組件是一個(gè)函數(shù),并不是一個(gè)組件
高階組件就是一個(gè)簡單的函數(shù),它會(huì)接收一個(gè)React組件作為參數(shù),然后返回另一個(gè)React組件。
通常,HOC會(huì)使用一個(gè)能夠維護(hù)State或包含若干功能的類來包裝輸入的組件。高階組件是組件之間功能復(fù)用的最佳方式。

高階組件的實(shí)現(xiàn)方式
屬性代理
我們可以看到下面高階組件最簡單的實(shí)現(xiàn)方式。我們把一個(gè)組件作為參數(shù)傳入到我們的函數(shù)里,在我們的函數(shù)里我們定義了一個(gè)類繼承了React.Component,然后通過render的方法中通過return把傳入的組件返回。這樣我們就可以代理所有傳入的props,并且決定如何渲染他們。實(shí)際上高階組件就是原組件的父組件。這就是屬性代理的實(shí)現(xiàn)方式。
function ComponentHOC(WrappedComponent) {
return class extends React.Component {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
對比原生組件增強(qiáng)的項(xiàng):
- 可操作所有穿傳入的
props - 可操作組件的生命周期
- 可操作組件的
static方法 - 獲取
refs
反向繼承
反向繼承是返回一個(gè)組件,這個(gè)組件繼承傳入的組件,然后在render中調(diào)用繼承組件的render方法。由于繼承于原組件,能通過this訪問到原組件的生命周期,props,state,render等,相比屬性代理它能操作更多的屬性。
function ConsoleHOC(WrappedComponent) {
return class extends WrappedComponent {
render(){
return super.render();
}
}
}
對比原生組件增強(qiáng)的項(xiàng):
- 可操作所有傳入的
props - 可操作組件的生命周期
- 可操作組件的static方法
- 獲取refs
- 可操作state
- 可以渲染劫持
HOC可以實(shí)現(xiàn)什么功能
組合渲染--可使用任何其他的組件和原組件進(jìn)行組合渲染,達(dá)到樣式,布局復(fù)用等效果。
我們用兩種實(shí)現(xiàn)方式分別實(shí)現(xiàn)以下
//屬性代理的實(shí)現(xiàn)方式
function styleHOC(WrappedComponent){
return class extends Component{
render(){
return (
<div>
<div className="title">{this.props.title}</div>
<WrappedComponent {...this.props}/>
</div>
)
}
}
}
//反向繼承的實(shí)現(xiàn)方式
function styleHOC(WrappedComponent){
return class extends WrappedComponent{
render(){
return (
<div>
<div className="title">{this.props.title}</div>
{super.render()}
</div>
)
}
}
}
條件渲染--根據(jù)特定的屬性決定原組件是否渲染
//通過屬性代理的方式實(shí)現(xiàn)
function visibleHOC(WrappedComponent){
return class extends Component {
render(){
if(this.props.visible === false) return null
return <WrappedComponent {...this.props}/>
}
}
}
//通過反向繼承的方式實(shí)現(xiàn)
function visibleHOC(WrappedComponent){
return class extends WrappedComponent{
render(){
if(this.props.visible === false){
return null
}else{
return super.render()
}
}
}
}
操作Props--可以對傳入的props進(jìn)程增加,修改,刪除或者根據(jù)特定的Props進(jìn)行特殊的操作:
//通過屬性代理的方式實(shí)現(xiàn)
function proxyHOC(WrappedComponent){
return class extends Component{
render(){
const newProps = {...this.props,user:'張三'};
return <WrappedComponent {...newProps}/>
}
}
}
//通過反向繼承實(shí)現(xiàn)
function proxyHOC(WrappedComponent){
return class extends WrappedComponent{
render(){
const newProps = {...this.props,user:'張三'};
//doSomething...
return super.render();
}
}
}
獲取refs
我們通過獲取原組件的ref來操作原組件的一些方法。關(guān)于獲取ref可以參考官網(wǎng)的Refs & DOM這一章節(jié)。從官網(wǎng)中我們知道,現(xiàn)在它推薦使用兩種方式來獲取refs
第一種是使用:React.createRef()
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
第二種是使用:回調(diào)
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = null;
this.setRef=element=>{
this.myRef = element;
}
}
render() {
return <div ref={this.setRef} />;
}
}
我們的高階函數(shù)同樣可以使用上面的方式。
//通過屬性代理實(shí)現(xiàn)
function refHOC(WrappedComponent) {
return class extends Component {
this.wapperRef = null;
componentDidMount() {
this.wapperRef.log()
}
render() {
return <WrappedComponent {...this.props} ref={ref => { this.wapperRef = ref }} />;
}
}
}
狀態(tài)管理
將原組件的狀態(tài)提取到HOC中進(jìn)行管理,如下面的代碼。我們將Input的value提取到HOC中進(jìn)行管理,使它變成受控組件,同時(shí)不影響它使用onChange方法進(jìn)行一些其他操作。
//通過屬性代理的方式實(shí)現(xiàn)
function proxyHoc(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
}
onChange = (event) => {
const { onChange } = this.props;
this.setState({
value: event.target.value,
}, () => {
if(typeof onChange ==='function'){
onChange(event);
}
})
}
render() {
const newProps = {
value: this.state.value,
onChange: this.onChange,
}
return <WrappedComponent {...this.props} {...newProps} />;
}
}
}
class HOC extends Component {
render() {
return <input {...this.props}></input>
}
}
export default proxyHoc(HOC);
獲取state
上面的方式實(shí)現(xiàn)上是高階組件也就是父組件把它自己的實(shí)現(xiàn)綁定到子組件的input組件上面了。并沒有直接去操作state.而通過反向繼承,我們可以直接操作原組件的state.但是并不推薦直接操作或添加state.容易出事兒。
//通過反向繼承實(shí)現(xiàn)
function debugHOC(WrappedComponent){
return class extends WrappedComponent{
render(){
console.log('props',this.props);
console.log('props',this.state);
return super.render();
}
}
}
上面是一個(gè)簡單的debug的高階組件,我們可以寫很多類似這樣的調(diào)試組件,在使用的時(shí)候直接@debug.多爽。
如何使用HOC
HOC實(shí)際上就是一個(gè)函數(shù),所以我們可以把它當(dāng)成函數(shù)那樣使用:
function debugHOC(WrappedComponent){
return class extends WrappedComponent{
render(){
console.log('props',this.props);
console.log('props',this.state);
return super.render();
}
}
}
class MyComponent extends Component{
render(){
return (<span>原組件</span>)
}
}
export default debugHOC(MyComponent);
Decorators 裝飾器模式
我們可以借助ES7為我們提供的Decorators來讓我們的寫法變的更加優(yōu)雅:
@logger
@visible
@style
class Input extends Component {
// ...
}
Decorators是ES7的一個(gè)提案,還沒有被標(biāo)準(zhǔn)化,但目前Babel轉(zhuǎn)碼器已經(jīng)支持,我們需要提前配置babel-plugin-transform-decorators-legacy:
"plugins": ["transform-decorators-legacy"]
還可以結(jié)合compose函數(shù)使用:
const hoc = compose(logger,visible,style);
@hoc
class Input extends Component {
//...
}
我們暫時(shí)更新到這里...