從高階函式--->高階元件

李群彬發表於2018-05-20

前言

今天有幸去參加了下別人公司的分享會,我帶著想讓個人給我梳理下我對高階元件瞭解比較混亂的思路,但分享的內容跟我期望方向不在一個點上,所以結束後我還是想象,我自己來梳理下自己對高階元件淺顯的理解。希望大家給予指導

要講高階元件,先讓我介紹下高階函式,這樣類比下就很容易理解了。

高階函式:以函式作為引數的函式,結果return一個函式
(感謝有人指出錯誤,高階函式只要滿足引數或返回值為函式就可以成為高階函式,而非一定要同時滿足才成立)
高階元件:以元件作為引數的元件,結果return一個元件

一、高階函式

高階函式(Higher Order Function),按照維基百科上面的定義,至少滿足下列一個條件的函式

  • 函式作為引數傳入
  • 返回值為一個函式

簡單的例子:

function add(a,b,fn){
    return fn(a)+fn(b);
}
var fn=function (a){
  return a*a;
}
add(2,3,fn); //13
複製程式碼

還有一些我們平時常用高階的方法,如: Map、Reduce、Filter、Sort;
以及現在常用的redux中的connect方法也是高階函式。
注:一開始我都不知道這幾個常用的方法居然就是高階函式,拿map展示下

var pow = function square(x) {
    return x * x;
};

var array = [1, 2, 3, 4, 5, 6, 7, 8];
var newArr = array.map(pow); //直接傳入一個函式方法
var newArr = array.map((item) => {return item * item}); //裡面運用一個函式
//返回的一個函式
alert(newArr); // [1, 4, 9, 16, 25, 36, 49, 64]
複製程式碼

函式柯里化

在電腦科學中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數而且返回結果的新函式的技術。

對我的的理解就是函式裡面的函式分次傳進去,我自己寫了個非常簡單的例子。

var add = function(x) {
    return function(y) {
      return function(z){
        console.log('curr',x,y,z)
         return x+y+z;
        };
    };
  };

  console.log(add(1)(5)(5));//傳三個引數
  ///currying 1 5 5
  //11
  console.log(add(1)(5));//如果傳兩個引數,則會把第三個函式返回出來
  /*ƒ (z) {
            console.log('currying', x, y, z);
            return x + y + z;
          }
  */
  //如果多傳則會報錯
複製程式碼

所以,我對柯里化的運用場景還是比較少的。 不知為何, 我覺得上面那個栗子有點奇怪,所以,我又試了下面這個栗子,可以無限傳參;

  var add = function(action) {
    var sumFun = function(doing){
      
      var sum = '';
      if(action == 'sum'){
        console.log('befor',sum)
        if(doing){
          sum = doing;
        }else{
          sum = 'nothing'
        }

      }
      console.log('lastSum='+sum);
      return sumFun ;
    }
    return sumFun ;
  };
  
add('sum')(2)(3)();//2  3 nothing;
add('sum')(2)(3)(4)(5);// 2 3 4 5
複製程式碼

ES6的中的寫法其實會比較清晰

var add2 = x => y => x+y;
var add3 = add2(2)

 console.log('add2',add2(2)) //返回一個方法,不呼叫後面的函式
 console.log('add3',add3(5)) //7
 
// 分步傳參,第二次呼叫時才會去執行函式。
複製程式碼

總結(我的理解):柯里化函式就是一種分步傳參的函式,可以提前傳參而不讓他執行內容,但是引數滿足時再呼叫函式。感覺可以用來做一些未知的判斷。

二、高階元件

高階元件就是一個 React 元件包裹著另外一個 React 元件。


// It's a function...
function myHOC() {
  // Which returns a function that takes a component...
  return function(WrappedComponent) {
    // It creates a new wrapper component...
    class TheHOC extends React.Component {
      render() {
        // And it renders the component it was given
        return <WrappedComponent {...this.props} />;
      }
    }

    // Remember: it takes a component and returns a new component
    // Gotta return it here.
    return TheHOC;
  }
}
複製程式碼

如圖,高階元件是個純函式。 接受一個元件引數,然後在return裡面是返回一個元件。 用來把兩個類似的元件進行'封裝'?(不知用這個詞是否合適)然後在高階元件裡面把公共部分寫好,然後傳給元件。

來個好理解的例子吧

//Welcome 元件
import React, {Component} from 'react'

class Welcome extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }

    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        })
    }

    render() {
        return (
            <div>welcome {this.state.username}</div>
        )
    }
}

export default Welcome;
複製程式碼
//goodbye 元件
import React, {Component} from 'react'

class Goodbye extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }

    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        })
    }

    render() {
        return (
            <div>goodbye {this.state.username}</div>
        )
    }
}

export default Goodbye;
複製程式碼

從上述可見, 這兩個元件雖然功能不一樣, 但是兩個元件有許多一樣的程式碼,這樣感覺就非常的多餘。所以,我們就可以做一個高階元件來整合。

import React, {Component} from 'react'

export default (WrappedComponent) => {
    class NewComponent extends Component {
        constructor() {
            super();
            this.state = {
                username: ''
            }
        }
      
        componentWillMount() {
            let username = localStorage.getItem('username');
            this.setState({
                username: username
            })
        }

        render() {
            return <WrappedComponent username={this.state.username}/>
        }
    }

    return NewComponent
}
複製程式碼

然後分別呼叫高階元件

//高階的Welcome元件
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';

class Welcome extends Component {

    render() {
        return (
            <div>welcome {this.props.username}</div>
        )
    }
}

Welcome = wrapWithUsername(Welcome);

export default Welcome;

//高階的goodbye元件
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';

class Goodbye extends Component {

    render() {
        return (
            <div>goodbye {this.props.username}</div>
        )
    }
}

Goodbye = wrapWithUsername(Goodbye);

export default Goodbye;
複製程式碼

注意

1、官方推薦在高階元件裡儘量不要設定state值,傳值的話可以用props的方法。 2、儘量不去改變原始元件,而是通過組合的方式。

提供參考的博文地址: JS中的柯里化(currying)-張鑫旭
React高階元件(譯)
助你完全理解React高階元件

相關文章