1. 程式人生 > >react高階元件淺談

react高階元件淺談

引入及概念

1.js中高階函式:一個函式就可以接收另一個函式作為引數,這種函式就稱之為高階函式。

function add(x, y, f) {
    return f(x) + f(y);
}
//當呼叫add(-5, 6, Math.abs)時,引數x,y和f分別接收-5,6和函式Math.abs,根據函式定義,可以推導計算過程為:
//x = -5;
//y = 6;
//f = Math.abs;
//f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;
//return 11;

//用程式碼驗證一下:
add(-5, 6, Math.abs); // 11

2.類似於高階函式,高階元件(Higher-Order Components)概念如下:高階元件就是一個函式,且該函式接受一個元件作為引數,並返回一個新的元件

const EnhancedComponent = higherOrderComponent(WrappedComponent);

3.應用場景

(1)react-redux的connect函式~

//把redux的state和action傳進去用來建立高階函式,
//通過props注入給了Component。
//被包裝後的Component可以直接用this.props去呼叫redux state和action建立函數了。
ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component);

即分解如下:

// connect是一個返回函式的函式(就是個高階函式)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函式就是一個高階元件,該高階元件返回一個與Redux store
// 關聯起來的新元件
const ConnectedComment = enhance(Component);

(2)antd中form表單包裝

const WrappedNormalLoginForm = Form.create()(NormalLoginForm);

4.高階元件的意義

高階元件就是一個沒有副作用的純函式。

最普通的元件哦。
welcome函式轉為react元件。

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;

goodbey函式轉為react元件。

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;

問題:上面兩段程式碼相同,都市從localstorange中拿到username,解決:寫一個通用的高階元件。

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
}

使用高階函式封裝元件:

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;
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;

高階元件就是把username通過props傳遞給原始元件了。原始元件只管從props裡面拿來用就好了。

使用注意

1.不要在render裡使用高階元件。

原因:效能問題+重新載入一個元件會引起原有元件的所有狀態和子元件丟失

解決:元件外使用(推薦)、建構函式裡、生命週期裡使用

render() {
  // 每一次render函式呼叫都會建立一個新的EnhancedComponent例項
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // 每一次都會使子物件樹完全被解除安裝或移除
  return <EnhancedComponent />;
}

2.必須將靜態方法做拷貝

當使用高階元件包裝元件,原始元件被容器元件包裹,也就意味著新元件會丟失原始元件的所有靜態方法。

解決:

(1)將原始元件的所有靜態方法全部拷貝給新元件:

function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  // 必須得知道要拷貝的方法 :(
  Enhance.staticMethod = WrappedComponent.staticMethod;
  return Enhance;
}

點評:你需要清楚的知道都有哪些靜態方法需要拷貝

(2)使用hoist-non-react-statics自動拷貝所有非React的靜態方法:

import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  hoistNonReactStatic(Enhance, WrappedComponent);
  return Enhance;
}

(3)分別匯出元件靜態方法

// 替代……
MyComponent.someFunction = someFunction;
export default MyComponent;

// ……分別匯出……
export { someFunction };

// ……在要使用的元件中匯入
import MyComponent, { someFunction } from './MyComponent.js';

3.不能傳遞refs屬性

refs是一個偽屬性,React對它進行了特殊處理。如果你向一個由高階元件建立的元件的元素新增ref應用,那麼ref指向的是最外層容器元件例項的,而不是內層包裹元件。

使用例子後續補充。。。^_^

參考連結:

歡迎指正!