react系列-bind this

問題


對於大多數前端開發來講,JavaScript 的 this 關鍵字會形成諸多困擾,因爲 JavaScript 代碼中的 this 指向並不清晰。在寫react應用時,也會也到不少做用域綁定引發的問題,React組件ES6的寫法,不會將方法內部的做用域自動綁定到組件的實例上。javascript

下面展現一段問題代碼前端

class Search extends Component {
    static propTypes = {
        onSearch: React.PropTypes.func.isRequired
    }
    onSearch() {
        console.log('表單值:', this.field.getValues());
        this.props.onSearch(this.field.getValues());
    }
    render(){
        const {init} = this.field;
        return <div>
            <Form direction="hoz" labelAlign="left">
                    <FormItem label="loginID:">
                        <Input placeholder="請輸入loginID" {...init('loginID')}/>
                    </FormItem>
                    <Button type="primary" onClick={this.onSearch}>搜索</Button>
            </Form>
        </div>
    }
}

若是你真的嘗試這麼作了, 你會發如今onSearch中,由於this指向的是全局對象window而報錯。java

解決辦法


咱們都知道常規改變函數做用域的無非3種(Fiontion.prototype.bind call apply 三兄弟),下面講解一下在es6中bind做用域的幾種方式。react

  1. 使用Function.prototype.bind()git

    class Search extends Component {
        render(){
            return <div>
                <Form direction="hoz" labelAlign="left">
                        <FormItem label="loginID:">
                            <Input placeholder="請輸入loginID" {...init('loginID')}/>
                        </FormItem>
                        <Button type="primary" onClick={this.onSearch.bind(this)}>搜索</Button>
                </Form>
            </div>
        }
    }
  2. ES7函數綁定語法
    在 ES7 中,有一個關於 bind 語法 的提議,提議將 :: 做爲一個新的綁定操做符, 並且已經收錄在stage-0提案中,實際上::是Function.propotype.bind()的一種語法糖。 幸運的是,Babel已經提供了對這個新語法的支持。es6

    class Search extends Component {
        render(){
            return <div>
                <Form direction="hoz" labelAlign="left">
                        <FormItem label="loginID:">
                            <Input placeholder="請輸入loginID" {...init('loginID')}/>
                        </FormItem>
                        <Button type="primary" onClick={::this.onSearch}>搜索</Button>
                </Form>
            </div>
        }
    }
  3. 在構造函數中bind thisgithub

    class Search extends Component {
        constructor(props) {
            super(props);
            this.onSearch = this.onSearch.bind(this)
        }
        render(){
            return <div>
                <Form direction="hoz" labelAlign="left">
                        <FormItem label="loginID:">
                            <Input placeholder="請輸入loginID" {...init('loginID')}/>
                        </FormItem>
                        <Button type="primary" onClick={this.onSearch}>搜索</Button>
                </Form>
            </div>
        }
    }
  4. 使用箭頭函數app

    class Search extends Component {
        render(){
            return <div>
                <Form direction="hoz" labelAlign="left">
                        <FormItem label="loginID:">
                            <Input placeholder="請輸入loginID" {...init('loginID')}/>
                        </FormItem>
                        <Button type="primary" onClick={(...args)=>{
                            this.onSearch( ...args)
                        }}>搜索</Button>
                </Form>
            </div>
        }
    }
  5. core-decorators.js
    core-decorators.js爲開發者提供了一些實用的 decorator,其中實現的autobind修飾器能使得方法中的this對象綁定到原始對象函數

class Search extends Component {
    @autobind
    onSearch() {
        console.log('表單值:', this.field.getValues());
        this.props.onSearch(this.field.getValues());
    }
    render(){
        const {init} = this.field;
        return <div>
            <Form direction="hoz" labelAlign="left">
                    <FormItem label="loginID:">
                        <Input placeholder="請輸入loginID" {...init('loginID')}/>
                    </FormItem>
                    <Button type="primary" onClick={this.onSearch}>搜索</Button>
            </Form>
        </div>
    }
}

總結


比較

這裏咱們討論下以上幾種將this綁定到react組件方案的缺點,優勢本身體會吧。
方案1和方案2,缺點也很嚴重,這種方式破壞了組件的pure render,每次組件render時,子組件Button的onClick值都是從新賦值所得,會致使Button作一次無謂的render。並且函數綁定語法::屬於es7草案中的特性,還沒有歸入es標準。使用須要謹慎。
方案3和方案4會增長代碼量
方案5須要引入第三方庫,不過core-decorators.js提供了不少使用的裝飾器。ui

場景

某些場景下,咱們須要傳遞額外的參數,好比列表中刪除操做,須要傳id。經常使用的方案是方案1和方案4

// Function.prototype.bind()
<Item onClick={this.doDelete.bind(this, id)}>刪除</Item>
// 箭頭函數
<Item onClick={(...args)=>{
    this.doDelete(id, ...args)
}}>刪除</Item>
相關文章
相關標籤/搜索